const short D_RespawnTime = 300;
const short D_ScoreLimit = 20;

public static Random rnd = new Random();

public static List<Fighter> Fighters = new List<Fighter>();

public bool IsGameOvered = false;

public static int BlueScore = 0;
public static int RedScore = 0;

public static short BlueSize = 0;
public static short RedSize = 0;

Events.UserMessageCallback m_userMessageCallback = null;

//Colours

static Color ltGray = new Color(206, 206, 206);
static Color Red = new Color(255, 38, 38);
static Color Blue = new Color(38, 128, 255);
static Color Orange = new Color(255, 157, 38);

public void OnUserMessage(UserMessageCallbackArgs args){
	if(args.IsCommand){
		if(args.Command == "CHANGECLASS"){
			int newClass = 0;
			if(Int32.TryParse(args.CommandArguments, out newClass)){
				if(newClass >= 1 && newClass <= 8){
					foreach(Fighter o in Fighters){
						if(o.GetUser().Name == args.User.Name){
							o.GetPlayer().Kill();
							o.SetClass(newClass);
						}
					}
				}
			}else{
				Game.ShowChatMessage("[!] Invalid Class",Orange,args.User.UserIdentifier);
			}
		}
		if(args.Command == "CLASSES"){
			Game.ShowChatMessage("Classes:",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage("[1] - Scout",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[2] - Demoman",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[3] - Pyro",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[4] - Heavy",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[5] - Medic",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[6] - Support",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[7] - Sniper",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("[8] - Spy",ltGray,args.User.UserIdentifier);
		}
		if(args.Command == "CHANGETEAM"){
			if(args.CommandArguments.ToUpper() == "RED" || args.CommandArguments.ToUpper() == "BLUE"){
				int index = 0;
				if(args.CommandArguments.ToUpper() == "RED"){
					index = 0;
				}else{	
					index = 1;
				}	
				foreach(Fighter o in Fighters){
					if(o.GetUser().Name == args.User.Name){
						o.JoinTeam(index);
					}
				}
			}
		}
		if(args.Command == "TFHELP"){
			Game.ShowChatMessage("'/TFMOD' - Get More Info On The Gamemode",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage("'/CLASSES' - Show All Available Classes",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage("'/CHANGECLASS [x]' - Change Class To x",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage("'/CHANGETEAM [x]' - Change Team To [Red/Blue]",Orange,args.User.UserIdentifier);
		}
		if(args.Command == "TFMOD"){
			Game.ShowChatMessage("[Team Fortress]",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage(">Based On The Mod From Half-Life",Orange,args.User.UserIdentifier);
			Game.ShowChatMessage("Objective:",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("-Work together in Team Deathmatch",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("-Select from 8 classes and take on roles",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("-Eliminate opposing forces",ltGray,args.User.UserIdentifier);
			Game.ShowChatMessage("-Reach the score limit to win the match",ltGray,args.User.UserIdentifier);
		}
	}
}

public void OnStartup(){
	IObject[] Spawns = GetSpawns();
	for(int i = 0;i < 3;i ++){
		int SpawnLoc = rnd.Next(0,Spawns.Length);
		IObjectMedicalCabinetTrigger obj = (IObjectMedicalCabinetTrigger)Game.CreateObject("MedicalCabinet00",Spawns[SpawnLoc].GetWorldPosition());
		obj.SetEnabled(false);
		obj.CustomID = "GiveAll";
	}

	m_userMessageCallback = Events.UserMessageCallback.Start(OnUserMessage);

	Game.DeathSequenceEnabled = false;
	Game.AutoVictoryConditionEnabled = false;
	Game.AutoSpawnSupplyCratesEnabled = false;
	Game.AutoScoreConditionEnabled = false;

	foreach(IPlayer ply in Game.GetPlayers()){
		ply.Remove();
	}

	IObjectTimerTrigger rep = (IObjectTimerTrigger)Game.CreateObject("TimerTrigger");
	rep.SetScriptMethod("Tick");
	rep.SetRepeatCount(0);
	rep.SetIntervalTime(0);
	rep.Trigger();

	Game.RunCommand("/msg ----------------|");
	Game.RunCommand("/msg Welcome To Team Fortress");
	Game.RunCommand("/msg Fight With Your Team And Achieve Victory");
	Game.RunCommand("/msg The Score Limit Is " + D_ScoreLimit);
	Game.RunCommand("/msg ----------------|");
}

public void Tick(TriggerArgs args){
	CheckUsers();
	foreach(Fighter o in Fighters){
		o.Step();
	}

	if((Game.IsGameOver || RedScore >= D_ScoreLimit || BlueScore >= D_ScoreLimit) && !IsGameOvered){
		IsGameOvered = true;
		PlayerTeam WinTeam = PlayerTeam.Independent;
		if(RedScore > BlueScore){
			Game.SetGameOver("Red Team Wins!");
			WinTeam = PlayerTeam.Team2;
		}else if(RedScore < BlueScore){
			Game.SetGameOver("Blue Team Wins!");
			WinTeam = PlayerTeam.Team1;
		}else{
			Game.SetGameOver("It Was A Tie!");
		}
		foreach(Fighter o in Fighters){
			if(o.GetTeam() == WinTeam){
				o.GetUser().IncreaseScore();
			}
		}
	}

	Game.ShowPopupMessage("|------------|\nRed Score: " + RedScore + "\nBlue Score: " + BlueScore + "\n|------------|");
}

public void CheckUsers(){
	//Add New Users
	foreach(IUser user in Game.GetActiveUsers()){
		bool check = false;

		foreach(Fighter o in Fighters){
			if(user == o.GetUser()){
				check = true;
			}
		}

		if(!check){
			Fighters.Add(new Fighter(user));
		}
	}

	//Remove Removed Users
	for(int i = 0;i < Fighters.Count;i ++){
		Fighter o = Fighters[i];
		if(o.GetUser().IsRemoved){
			o.Clear();
			Fighters.Remove(o);
		}
	}
}

public class Fighter{

	IUser User = null;

		//Player Info
	IPlayer Player = null;
	PlayerTeam Team;
	short Class = 0;

		//Timers
	int AbilityTimer = 0;
	short RespawnTime = 0;	
	int ResupplyTimer = 0;

		//Custom Shots
	int Lshot = 0;
	int Fired = 0;
	int HipFired = 0;
	
		//Hidden Objects
	IObjectActivateTrigger trig = null;
	IObject Obj = null;
	
	public Fighter(IUser user){
		User = user;
		AutoTeam();
		SetClass(rnd.Next(1,9));
		SpawnPlayer();

			//Ability Trigger
		trig = (IObjectActivateTrigger)Game.CreateObject("ActivateTrigger");
		trig.CustomID = user.Name;
		trig.SetScriptMethod("Ability");

			//Gun Limiter
		Obj = Game.CreateObject("InvisibleBlock");	
		Obj.CustomID = "REMOVE_LATER";	
		Obj.SetSizeFactor(new Point(2,2));		
	}

	public void Clear(){
		if(Player != null && !Player.IsDead && !Player.IsRemoved){
			Player.Kill();
		}
		trig.Remove();
		Obj.Remove();
		
		switch(Team){
			case PlayerTeam.Team1:
				BlueSize --;
			break;	
			case PlayerTeam.Team2:
				RedSize --;
			break;
		}
	}

	public void Step(){
		Obj.SetWorldPosition(new Vector2(0,10000));
		CollisionFilter Col = Obj.GetCollisionFilter();
		Col.AboveBits = 0;
		Col.CategoryBits = 0;
		Col.MaskBits = 0;
		Col.BlockMelee = false;
		Col.ProjectileHit = true;
		Obj.SetCollisionFilter(Col);		
	
		if(AbilityTimer > 0){
			AbilityTimer --;
		}
		if(ResupplyTimer > 0){
			ResupplyTimer --;
		}
		if(Player != null && !Player.IsDead && !Player.IsRemoved){
			trig.SetWorldPosition(Player.GetWorldPosition() + new Vector2(0,8));
			
			if(Lshot != Player.Statistics.TotalShotsFired){
				Lshot = Player.Statistics.TotalShotsFired;
				Fired = 50;
			}else if(Fired > 0){
				Fired --;
			}
			if(Player.IsHipFiring){
				HipFired = 50;
			}else if(HipFired > 0){
				HipFired --;
			}
			if(!Player.IsHipFiring){
				HipFired = 0;
			}
			if(Player.CurrentWeaponDrawn == WeaponItemType.Rifle){
				if(Player.CurrentPrimaryWeapon.WeaponItem == WeaponItem.DARK_SHOTGUN){
					if(Fired > 49 || HipFired > 30){	
						int Crouch = 0;
						if(Player.IsCrouching){
							Crouch = 1;
						}
						Obj.SetWorldPosition(Player.GetWorldPosition() + new Vector2(-4,16 - 8 * Crouch) + Player.AimVector * 20);
					}
					if(Fired == 50){

						switch(Class){
							case 2:
								IObject obj = Game.CreateObject("WpnGrenadesThrown",Player.GetWorldPosition() + new Vector2(0,12) + Player.AimVector * 20);
								obj.SetLinearVelocity(Player.AimVector * 10);
								obj.CustomID = User.Name + "G";							
							break;
						}		
				
					}
				}
			}			
			
			//Check Spy Status
			if(Class == 8){
				if(Player.IsMeleeAttacking || Player.IsKicking || Player.IsJumpAttacking || Player.IsManualAiming || Player.IsHipFiring || Player.IsBurning){
					if(Player.GetTeam() != Team){
						IProfile prof = GetProfiles(Class,Team);
						Player.SetProfile(prof);
						Player.SetTeam(Team);
						AbilityTimer = 500;
					}					
				}
				if(Player.CurrentWeaponDrawn == WeaponItemType.Melee){
					PlayerModifiers mods = Player.GetModifiers();
					mods.MeleeDamageDealtModifier = 4;
					Player.SetModifiers(mods);
				}else{
					PlayerModifiers mods = Player.GetModifiers();
					mods.MeleeDamageDealtModifier = 0.5f;
					Player.SetModifiers(mods);
				}
			}

		}else{
			if(RespawnTime == 0){
				switch(Team){
					case PlayerTeam.Team1:
						RedScore ++;
					break;
					case PlayerTeam.Team2:
						BlueScore ++;
					break;
				}
				RespawnTime = D_RespawnTime;
			}
			if(RespawnTime > 0){
				RespawnTime --;
			}
			if(RespawnTime == 1){
				RespawnTime = 0;
				SpawnPlayer();
			}
		}
	}

	public void SpawnPlayer(){
		AbilityTimer = 0;
		ResupplyTimer = 0;
		
		IObject[] Spawns = GetSpawns();
		int SpawnLoc = rnd.Next(0,Spawns.Length);

		PlayerModifiers mod = GetModifiers(Class);
		IProfile prof = GetProfiles(Class,Team);

		Player = Game.CreatePlayer(Spawns[SpawnLoc].GetWorldPosition());
		Player.SetModifiers(mod);
		Player.SetProfile(prof);
		Player.SetTeam(Team);
		Player.SetFaceDirection(Spawns[SpawnLoc].GetFaceDirection());
		GiveWeapons(Player,Class);

		User.SetPlayer(Player);
		if(User.IsBot){
			Player.SetBotBehavior(new BotBehavior(true,User.BotPredefinedAIType));
		}
	}

	public IPlayer GetPlayer(){
		return Player;
	}

	public PlayerTeam GetTeam(){
		return Team;
	}

	public short GetClass(){
		return Class;
	}

	public IUser GetUser(){
		return User;
	}

	public void AutoTeam(){
		int index = rnd.Next(0,2);
		int TotalSize = Game.GetActiveUsers().Length;
		int TotalRedSize = TotalSize / 2;
		int TotalBlueSize = TotalSize / 2 + TotalSize % 2;

		if(index == 0){
			if(RedSize < TotalRedSize){
				RedSize ++;
				Team = PlayerTeam.Team2;
			}else{
				BlueSize ++;
				Team = PlayerTeam.Team1;
			}
		}else{
			if(BlueSize < TotalBlueSize){
				BlueSize ++;
				Team = PlayerTeam.Team1;
			}else{
				RedSize ++;
				Team = PlayerTeam.Team2;
			}			
		}
	}

	public void JoinTeam(int index){
		int TotalSize = Game.GetActiveUsers().Length;	
		
		if(index == 0){

			if(RedSize < BlueSize && Team != PlayerTeam.Team2){
				Player.Kill();
				Team = PlayerTeam.Team2;
				Game.ShowChatMessage(User.Name + " Joined Team Red",Red);
				SpawnPlayer();
				RedSize ++;
				BlueSize --;
			}else{
				Game.ShowChatMessage("Unable To Join Red Team",Orange,User.UserIdentifier);
			}	
		}else{

			if(RedSize > BlueSize && Team != PlayerTeam.Team1){
				Player.Kill();
				Team = PlayerTeam.Team1;
				Game.ShowChatMessage(User.Name + " Joined Team Blue",Blue);
				SpawnPlayer();
				BlueSize ++;
				RedSize  --;
			}else{
				Game.ShowChatMessage("Unable To Join Blue Team",Orange,User.UserIdentifier);
			}			
		}
	}

	public void UseAbility(){
		bool Use = true;

		//Check For Resuply Cabinets
		Vector2 art = Player.GetWorldPosition();
		Area ar = new Area(art.Y + 5,art.X - 5,art.Y - 5,art.X + 5);
		foreach(IObject obj in Game.GetObjectsByArea(ar)){
			if(obj.CustomID == "GiveAll"){
				if(ResupplyTimer == 0){
					GiveWeapons(Player,Class);

					//Restore 50 HP
					PlayerModifiers mods = Player.GetModifiers();
					mods.CurrentHealth += 50;
					Player.SetModifiers(mods);
					
					ResupplyTimer = 3000;
					Use = false;
				}else{
					Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"Recharging Cabinet %" + Math.Round((Math.Abs(3000 - (double)ResupplyTimer) / 3000) * 100));
					Use = false;
				}
			}
		}

	
		if(Use){
			switch(Class){
				case 2:
					foreach(IObject obj in Game.GetObjectsByCustomID(User.Name + "G")){
						obj.Destroy();
					}
				break;
				case 5:
					if(AbilityTimer == 0){
						IObject obj = Game.CreateObject("ItemMedkit",Player.GetWorldPosition());
						obj.SetLinearVelocity(new Vector2(Player.FacingDirection,0));
						AbilityTimer = 400;
						Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"MediKit Created");
					}else{
						Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"HealthKit Recharging %" + Math.Round((Math.Abs(400 - (double)AbilityTimer) / 400) * 100));
					}
				break;
				case 6:
					if(AbilityTimer == 0){
						Vector2 pos = Player.GetWorldPosition();
						foreach(Fighter o in Fighters){
							if(o.GetTeam() == Team){
								Vector2 trg = o.GetPlayer().GetWorldPosition();
								if(Math.Abs(Math.Abs(pos.X) - Math.Abs(trg.X)) <= 48 && Math.Abs(Math.Abs(pos.Y) - Math.Abs(trg.Y)) <= 32){
									IPlayer target = o.GetPlayer();
									if(target != null && !target.IsDead && !target.IsRemoved){
										GiveWeapons(target,o.GetClass());
										AbilityTimer = 400;
										Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"Ammo Sent");
									}
								}
							}
						}
					}else{
						Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"Ammo Recharging %" + Math.Round((Math.Abs(400 - (double)AbilityTimer) / 400) * 100));
					}
				break;
				case 8:
					if(Player.GetTeam() == Team && AbilityTimer == 0){
						switch(Team){
							case PlayerTeam.Team1:
								Player.SetTeam(PlayerTeam.Team2);
							break;
							case PlayerTeam.Team2:
								Player.SetTeam(PlayerTeam.Team1);
							break;
						}
						IProfile prof = GetProfiles((short)rnd.Next(1,9),Player.GetTeam());
						Player.SetProfile(prof);
					}else if(Player.GetTeam() != Team && AbilityTimer == 0){
						IProfile prof = GetProfiles(Class,Team);
						Player.SetProfile(prof);
						Player.SetTeam(Team);
						AbilityTimer = 500;
					}else{
						Game.PlayEffect("CFTXT",Player.GetWorldPosition() + new Vector2(0,8),"Disguise Recharging %" + Math.Round((Math.Abs(500 - (double)AbilityTimer) / 500) * 100));
					}
				break;
			}
		}
	}
	
	public void SetClass(int index){
		Class = (short)index;

		switch(Class){
			case 1:
				Game.ShowChatMessage("Changed class to Scout",Orange,User.UserIdentifier);
			break;
			case 2:
				Game.ShowChatMessage("Changed class to Demoman",Orange,User.UserIdentifier);
				Game.ShowChatMessage("Primary fires grenades, press [F] to detonate",Orange,User.UserIdentifier);
			break;
			case 3:
				Game.ShowChatMessage("Changed class to Pyro",Orange,User.UserIdentifier);
			break;
			case 4:
				Game.ShowChatMessage("Changed class to Heavy",Orange,User.UserIdentifier);
			break;
			case 5:
				Game.ShowChatMessage("Changed class to Medic",Orange,User.UserIdentifier);
				Game.ShowChatMessage("Press [F] to provide health",Orange,User.UserIdentifier);
			break;
			case 6:
				Game.ShowChatMessage("Changed class to Support",Orange,User.UserIdentifier);
				Game.ShowChatMessage("Press [F] to provide ammo to nearby teammates",Orange,User.UserIdentifier);
			break;
			case 7:
				Game.ShowChatMessage("Changed class to Sniper",Orange,User.UserIdentifier);
			break;
			case 8:
				Game.ShowChatMessage("Changed class to Spy",Orange,User.UserIdentifier);
				Game.ShowChatMessage("Press [F] To disguise as an enemy class",Orange,User.UserIdentifier);
			break;
		}
	}
}

public static IObject[] GetSpawns(){
	return Game.GetObjectsByName("SpawnPlayer");
}

public static PlayerModifiers GetModifiers(short index){
	PlayerModifiers mods = new PlayerModifiers(true);
	mods.ItemDropMode = 2;
	mods.MaxEnergy = 1;
	mods.EnergyConsumptionModifier = 0;
	mods.RunSpeedModifier = 1.25f;

	switch(index){
		case 1:
			mods.MaxHealth = 75;
			mods.CurrentHealth = 75;
			mods.RunSpeedModifier = 2;
		break;
		case 3:
			mods.RunSpeedModifier = 0.8f;
			mods.CanBurn = 0;
		break;
		case 4:
			mods.MaxHealth = 200;
			mods.CurrentHealth = 200;
			mods.RunSpeedModifier = 0.75f;
			mods.MeleeDamageDealtModifier = 2;
		break;
	}

	if(mods.RunSpeedModifier < 1){
		mods.RunSpeedModifier = 1;
	}else{
		mods.SprintSpeedModifier = mods.RunSpeedModifier;
	}

	return mods;
}

/*
*	1 - Scout	
*	2 - Demoman
*	3 - Pyro
*	4 - Heavy
*	5 - Medic
*	6 - Engineer
*	7 - Sniper
*	8 - Spy
*/
public static IProfile GetProfiles(short index,PlayerTeam Team){
	string col = "Red";
	if(Team == PlayerTeam.Team1){
		col = "Blue";
	}

	IProfile prof = new IProfile();
	prof.ChestUnder = new IProfileClothingItem("LeatherJacketBlack","Clothing" + col,"ClothingDark" + col);
	prof.Legs = new IProfileClothingItem("PantsBlack","ClothingGray");
	prof.Feet = new IProfileClothingItem("Boots","ClothingGray");
	prof.Hands = new IProfileClothingItem("Gloves","ClothingDarkGray");
	
	switch(index){
		case 1:
			prof.Head = new IProfileClothingItem("Cap","ClothingGray");
		break;
		case 2:
			prof.ChestOver = new IProfileClothingItem("GrenadeBelt","ClothingDarkGray");
		break;
		case 3:
			prof.Waist = new IProfileClothingItem("AmmoBelt","ClothingDarkGray");
			prof.ChestOver = new IProfileClothingItem("AmmoBelt","ClothingGray");
			prof.Accessory = new IProfileClothingItem("GasMask","ClothingBlack");
		break;		
		case 4:
			prof.Waist = new IProfileClothingItem("CombatBelt","ClothingGray");
			prof.Head = new IProfileClothingItem("Headband","Clothing" + col);
		break;		
		case 5:
			prof.ChestUnder = new IProfileClothingItem("Shirt","ClothingWhite");
			prof.Accessory = new IProfileClothingItem("Armband","Clothing" + col);
		break;
		case 6:
			prof.Head = new IProfileClothingItem("WeldingHelmet","ClothingGray");
		break;
		case 7:
			prof.Head = new IProfileClothingItem("CowboyHat","ClothingBrown","ClothingGray");
		break;
		case 8:
			prof.Accessory = new IProfileClothingItem("Balaclava","Clothing" + col);
		break;
	}

	return prof;
}

public static void GiveWeapons(IPlayer Player,short index){
	switch(index){
		case 1:
			Player.GiveWeaponItem(WeaponItem.BAT);
			Player.GiveWeaponItem(WeaponItem.PISTOL45);
			Player.GiveWeaponItem(WeaponItem.SAWED_OFF);
			Player.GiveWeaponItem(WeaponItem.SAWED_OFF);
		break;		
		case 2:
			Player.GiveWeaponItem(WeaponItem.LEAD_PIPE);
			Player.GiveWeaponItem(WeaponItem.DARK_SHOTGUN);
			Player.GiveWeaponItem(WeaponItem.MINES);
		break;		
		case 3:
			Player.GiveWeaponItem(WeaponItem.AXE);
			Player.GiveWeaponItem(WeaponItem.FLAREGUN);
			Player.GiveWeaponItem(WeaponItem.FLAMETHROWER);
		break;
		case 4:
			Player.GiveWeaponItem(WeaponItem.TOMMYGUN);
		break;
		case 5:
			Player.GiveWeaponItem(WeaponItem.MACHINE_PISTOL);
		break;
		case 6:
			Player.GiveWeaponItem(WeaponItem.PIPE);
			Player.GiveWeaponItem(WeaponItem.SHOTGUN);
		break;
		case 7:
			Player.GiveWeaponItem(WeaponItem.MACHETE);
			Player.GiveWeaponItem(WeaponItem.SNIPER);
		break;
		case 8:
			Player.GiveWeaponItem(WeaponItem.KNIFE);
			Player.GiveWeaponItem(WeaponItem.REVOLVER);
		break;
	}
}

public void Ability(TriggerArgs args){
	if(((IPlayer)args.Sender).Name == ((IObject)args.Caller).CustomID){
		foreach(Fighter o in Fighters){
			if(o.GetUser().Name == ((IObject)args.Caller).CustomID){
				o.UseAbility();
			}
		}
	}
}