/*
 *=========================================
 *====== HUNTER MODE by Gwendalaze ========
 *=========================================
 */

/*
 *=========================================
 * Settings
 *=========================================
 */

public const float INITIALTIME = 60000f		; //Number of miliseconds you have in the beggining. DEFAULT : 60000
public const float TIMEDECREMENT = 5000f	; //Number of miliseconds removed everytime a player is removed from game. DEFAULT : 5000
public const float RESPTIME = 3000f	; //Time required to respawn a player (warning : do not remove the f !). DEFAULT : 3000f
public const int REFRESHRATE = 150; //Put it lower on better computer, higher on worst computer. DEFAUKT : 150

/*
 *=========================================
 * Variables and classes
 *=========================================
 */

public static HunterType hunterType;
public static PlayerData Hunter; 
public static List<PlayerData> Players = new List<PlayerData>();
public static float MaxTime;
public static float TimeRemaining;
public static Vector2 [] respawnPos;

public class HunterType{
	public string Name{get;private set;}
	public string AbDesc{get;private set;}
	public float SpecialCd{get;private set;}
	public float SpecialDate{get;private set;}
	public Special Ability{get;private set;}
	public WeaponItem SignatureWeapon{get;private set;}

	public delegate bool Special();

	public static Special Bash = new Special(Hunter_Bash);
	public static Special GetM16 = new Special(Hunter_GetM16);
	public static Special Matrix = new Special(Hunter_Matrix);

	public HunterType(string in_Name,string in_AbDesc, float in_Cd, Special in_sp, WeaponItem in_sw){
		this.Name = in_Name;
		this.SpecialCd = in_Cd;
		this.Ability = in_sp;
		this.AbDesc = in_AbDesc;
		this.SignatureWeapon = in_sw;
	}

	public void UseHunterAbility(){
		if (!Hunter.ply.IsDead && Hunter.ply != null && Hunter.ply.IsWalking && this.SpecialDate < Game.TotalElapsedGameTime){
			if (this.Ability())this.SpecialDate = Game.TotalElapsedGameTime + this.SpecialCd;
		}
	}

	public static HunterType Rambo = new HunterType("Rambo","to get an M16",10000,GetM16,WeaponItem.ASSAULT);
	public static HunterType Smith = new HunterType("Smith agent","to glitch the matrix",12000,Matrix,WeaponItem.SMG);
	public static HunterType Jason_Voorhees = new HunterType("Jason Voorhees"," and [KEY=KICK] to give a superkick", 2400,Bash,WeaponItem.MACHETE);

	public static HunterType [] HunterList = {Rambo,Jason_Voorhees,Smith};
}

public class PlayerData{

	public  IUser user {get; private set;}

	public IPlayer ply {get; private set;}

	public float DeathDate{get; private set;}

	public bool IsDead {get; private set;}

	public PlayerData(IPlayer in_ply){
		this.ply = in_ply;
		this.DeathDate = 0f;
		this.IsDead = false;
		this.user = in_ply.GetUser();
	}

	public void RespawnCheck(){
		if (this.IsDead && this.DeathDate  + RESPTIME < Game.TotalElapsedGameTime){
			Respawn();
		}
		if (!this.IsDead && (this.ply == null||this.ply.IsDead||this.ply.IsRemoved )){
			this.IsDead = true;
			this.DeathDate = Game.TotalElapsedGameTime;
			TurnToHunter();
		}
	}

	public void Respawn(){
		this.IsDead = false;
		this.ply = CreatePlayer(this.user);
		if (this == Hunter){
			ply.SetTeam(PlayerTeam.Team2);
			ply.GiveWeaponItem(hunterType.SignatureWeapon);
		} else {
			ply.SetHealth(50f);
		}
	}

	public void TurnToHunter(){
		if (Hunter != null && Hunter.ply != null && !Hunter.ply.IsDead){
			Hunter.ply.SetTeam(PlayerTeam.Independent);
			Vector2 Dest = Hunter.ply.GetWorldPosition();
			Game.PlayEffect("Electric",Dest);
			Game.PlayEffect("CFTXT",Dest,"Curse passed");
			Hunter.ply.SetHealth(50f);
		}
		Hunter = this;
	}
}


public static Random rnd = new Random();


/*
 *=========================================
 * Main method
 *=========================================
 */

public void OnStartup(){
	if (Game.GetMapType() != MapType.Custom){
		Game.RunCommand("/MSG Welcome to Gwendalaze's hunter mode. The hunter is red. If you are the hunter, kill the survivors. The hunter get eliminated at the end of the countdown");
		MaxTime = INITIALTIME;
		TimeRemaining = INITIALTIME;
		Game.SetMapType(MapType.Custom);	

		List<Vector2> respTemp = new List<Vector2>();
		foreach (IObject obj in Game.GetObjectsByName("SpawnPlayer")) respTemp.Add(obj.GetWorldPosition());
		respawnPos = respTemp.ToArray();
	
		CreateTimer("Clock","Tick",REFRESHRATE,0);
	
		InitializePlayers();
		InitializeHunter();

	} else {
		Game.RunCommand("/MSG Can not starts Hunter mode : Map restriction");
	}
}

public void StartRound(){
	RoundIsOver = false;
	Game.RunCommand("/INFINITE_LIFE 0");
	MaxTime -= TIMEDECREMENT;
	TimeRemaining = MaxTime;

	ChooseNewHunter();
}

bool RoundIsOver = false;

public void Tick(TriggerArgs args){
	
	if (hunterType != null && Hunter != null)hunterType.UseHunterAbility();

	if (!RoundIsOver){
		TimeRemaining -= REFRESHRATE;
		ShowTimeRemaining();
		if (TimeRemaining < 0){
			EndRound();
		}
	}

	foreach (PlayerData data in Players){
		data.RespawnCheck();
	}
}

public void EndRound(){
	RoundIsOver = true;
	Game.RunCommand("/INFINITE_LIFE 1");

	Game.ShowPopupMessage("Round over. " + Hunter.ply.Name + " is eliminated");

	Hunter.ply.Gib();
	Players.Remove(Hunter);
	Hunter = null;

	if (Players.Count > 1){
		CreateTimer("RoundCounter","RestartRound",5000,1);
	} else if (Players.Count > 0){
		Game.SetGameOver(Players[0].ply.Name+" wins !");
	} else {
		Game.SetGameOver("Draw");
	}
}

public void RestartRound(TriggerArgs args){
	Game.ShowPopupMessage("");
	((IObject)args.Caller).Remove();
	StartRound();
}

/*
 *=========================================
 * methods
 *=========================================
 */

public void InitializePlayers(){
	foreach (IPlayer ply in Game.GetPlayers()){
		ply.SetTeam(PlayerTeam.Independent);
		ply.SetHealth(50f);
		Players.Add(new PlayerData(ply));
	}
}

public void InitializeHunter(){
	hunterType =  HunterType.HunterList[rnd.Next(HunterType.HunterList.Length)];
	ChooseNewHunter();
	Game.RunCommand("/MSG The hunter is : " + Hunter.ply.Name);
	Hunter.ply.SetHealth(100f);
}

public void ChooseNewHunter(){
	PlayerData data = Players[rnd.Next(Players.Count)];
	IPlayer ply = data.ply;
	Vector2 Dest = ply.GetWorldPosition();
	data.TurnToHunter();
	ply.SetTeam(PlayerTeam.Team2);
	ply.GiveWeaponItem(hunterType.SignatureWeapon);
	Game.PlayEffect("Electric",Dest);
	Game.PlayEffect("CFTXT",Dest,"You are the hunter");
}

/*
 *=========================================
 * helpers
 *=========================================
 */

public void CreateTimer(string name,string method, int interval, int repeat){
	IObjectTimerTrigger timer = Game.CreateObject("TimerTrigger") as IObjectTimerTrigger;
	timer.CustomId = name;
	timer.SetScriptMethod(method);
	timer.SetIntervalTime(interval);
	timer.SetRepeatCount(repeat);
	timer.Trigger();
} 

public static IPlayer CreatePlayer(IUser user){
	IPlayer ply = Game.CreatePlayer(respawnPos[rnd.Next(respawnPos.Length)]);
	if (user != null){
		ply.SetProfile(user.GetProfile());
		ply.SetUser(user);
	}
	return ply;
}

public void ShowTimeRemaining(){
	int TimeToShow = (int)TimeRemaining/1000;
 	string Time = "";
	Time += TimeToShow / 60 + ":";
	Time += (TimeToShow % 60 >= 10)? (TimeToShow % 60).ToString() : "0"+TimeToShow % 60;
	Game.ShowPopupMessage(Time + " before hunter get out of play\n" +hunterType.Name + " : press [KEY=WALKING] " + hunterType.AbDesc);
}

/*
 *=========================================
 * hunter's special
 *=========================================
 */

public static bool Hunter_Bash(){
	IPlayer ply = Hunter.ply;
	if (ply.IsKicking || ply.IsJumpKicking){
		int dir = ply.FacingDirection;
		Vector2 pos = ply.GetWorldPosition();
		Area area = new Area(pos.Y+20f,pos.X-35f+(35f*dir),pos.Y-20f,pos.X+35f+(35f*dir));
		foreach (IObject obj in Game.GetObjectsByArea(area)){
			if (obj != ply){
				if (obj is IPlayer){
					obj.SetWorldPosition(obj.GetWorldPosition() + new Vector2(0f,8f));
				} 
				obj.SetLinearVelocity(new Vector2(dir * 12f, 4f));
			}
		}
		for (int x = 0; x< 8; x++) Game.PlayEffect("STM",pos + new Vector2(10f*x*ply.FacingDirection,10f));
		Game.PlayEffect("CAM_S",pos,5f,200f,true);
		Game.PlaySound("GrenadeThrow",pos,1f);
		Game.PlaySound("Shotgun",pos,0.25f);
		return true;
	} else {
		return false;
	}
}

public static bool Hunter_GetM16(){
	Hunter.ply.GiveWeaponItem(WeaponItem.ASSAULT);
	return true;
}

public static bool Hunter_Matrix(){
	List<Vector2> pos = new List<Vector2>();
	foreach (PlayerData data in Players){
		if (data.ply != null && !data.ply.IsDead && data != Hunter){
			pos.Add(data.ply.GetWorldPosition() + new Vector2(rnd.Next(-10,11),0f));
		}
	}
	if (pos.Count == 0)pos.Add(Hunter.ply.GetWorldPosition());
	Vector2 Dest = pos[rnd.Next(pos.Count)];
	Game.PlayEffect("Electric",Dest);
	Game.PlayEffect("Electric",Hunter.ply.GetWorldPosition());
	Game.PlaySound("PlayerGib",Dest,1f);
	Hunter.ply.SetWorldPosition(Dest);
	Hunter.ply.SetHealth(Hunter.ply.GetHealth() + 25f);
	Hunter.ply.SetLinearVelocity(Vector2.Zero);
	return true;
}