//Don't judge me, scripting in one file is hard :(
//Made by Lozango com z (Discord: Lozango com z#7177)

public static void Print(string str){Game.ShowChatMessage(str);}
public static Random rnd = new Random();

public static List<Organ> HealthFilter(List<Organ> OrgsList){
	return OrgsList.Where(
	Organ => GetAllHealth(Organ) > 0).ToList();
}
public static float GetAllHealth(Organ Org){return GetAllHealth(new List<Organ>{Org});}
public static float GetAllHealth(List<Organ> Orgs){
	float Result = 0;
	foreach(Organ Org in Orgs){
		Result+=Org.Health;
		foreach(Organ ChildOrg in GetAllChildOrgans(Org)){
			Result+=ChildOrg.Health;
		}
	};
	return Result;
}
public static float GetAllInitialHealth(Organ Org){return GetAllInitialHealth(new List<Organ>{Org});}
public static float GetAllInitialHealth(List<Organ> Orgs){
	float Result = 0;
	foreach(Organ Org in Orgs){
		Result+=Org.InitialHealth;
		foreach(Organ ChildOrg in GetAllChildOrgans(Org)){
			Result+=ChildOrg.InitialHealth;
		}
	};
	return Result;
}
public static List<Organ> GetAllChildOrgans(Organ Org){
	List<Organ> Result = new List<Organ>();
	for(Organ ChildOrg = Org.ChildOrgan;;ChildOrg = ChildOrg.ChildOrgan){
		if(ChildOrg == null)break;
		Result.Add(ChildOrg);
	}
	return Result;
}
public static List<Organ> GetAllChildOrgans(List<Organ> Orgs){
	List<Organ> Result = new List<Organ>();
	foreach(Organ Org in Orgs){
		Result.AddRange(GetAllChildOrgans(Org).ToArray());
	}
	return Result;
}

public class BodyProperties{
	public IPlayer Host;
	
	public List<StaticText> OrgansText = new List<StaticText>();
	public bool WasFalling = false;
	public void CheckWasFalling(){WasFalling = Host.IsFalling;}
	
	public PlayerModifiers OriginalPlayerModifiers;
	public IProfile OriginalIProfile;
	public void UpdateIProfile(){

		Host.SetProfile(OriginalIProfile);

		var TProfile = Host.GetProfile();
		
		foreach(BodyPart BdyPart in AllBodyParts.Values){
			if(BdyPart.Arm != null){
				System.Reflection.FieldInfo Field;
				
				for(int i = 0; i < BdyPart.Arm.VisualClothPartName.Length;i++){
					var VCPN = BdyPart.Arm.VisualClothPartName[i];
					
					if(Host.GetProfile().Gender == Gender.Female && (VCPN == "ChestOver" || VCPN == "ChestUnder" || VCPN == "Legs"))
						Field = typeof(IProfile).GetField(BdyPart.Arm.VisualClothPartName[i] + "_fem");
					
					else Field = typeof(IProfile).GetField(BdyPart.Arm.VisualClothPartName[i]);
					Field.SetValue(TProfile,BdyPart.Arm.VisualCloth[i]);
				}
				Host.SetProfile(TProfile);
			}
		}
		Host.SetProfile(TProfile);
	}
	
	//Constructor
	public BodyProperties(IPlayer Ply){
		Host = Ply;
		OriginalIProfile = Ply.GetProfile();
		OriginalPlayerModifiers = Host.GetModifiers();
		if(Host.GetModifiers().MaxHealth == -1) Host.GetModifiers().MaxHealth = 100;
		if(Host.GetModifiers().MaxEnergy == -1) Host.GetModifiers().MaxEnergy = 100;
		
		PlayerModifiers HostMod = Host.GetModifiers();
		HostMod.MaxHealth = (int)(HostMod.MaxHealth * GlobalMultipliers["HM"]);
		Host.SetModifiers(HostMod);

		MaxBlood = Host.GetModifiers().MaxHealth;
		Blood = MaxBlood;
		
		BodyPart HeadBodyPart = new BodyPart(this, new Area(50,-50,9,50),"Head",new Vector2(0,13)){HitChance = 0.7};
		BodyPart TorsoBodyPart = new BodyPart(this, new Area(10,-50,3,50),"Torso",new Vector2(0,8));
		BodyPart ArmsBodyPart = new BodyPart(this, new Area(8,-50,3,50),"Arms",new Vector2(0,8)){HitChance = 0.3};
		BodyPart LegsBodyPart = new BodyPart(this, new Area(4,-50,-50,50),"Legs",new Vector2(0,3)){HitChance = 0.3};
		
		
		Organ Brain = new Organ("Brain",3,Material.Flesh.Copy(),null,this){MainBodyPart = HeadBodyPart};
		Organ Skull = new Organ("Skull",10,Material.Bone.Copy(),Brain,this){MainBodyPart = HeadBodyPart};
		Organ Head = new Organ("Head",10,Material.Flesh.Copy(),Skull,this){MainBodyPart = HeadBodyPart};

		Organ Hearth = new Organ("Hearth",3,Material.Flesh.Copy(),null,this){MainBodyPart = TorsoBodyPart};
		Organ Ribs = new Organ("Ribs",12,Material.Bone.Copy(),Hearth,this){MainBodyPart = TorsoBodyPart};
		Organ Torso = new Organ("Torso",13,Material.Flesh.Copy(),Ribs,this){MainBodyPart = TorsoBodyPart};

		Organ LeftLegBone = new Organ("Left Leg Bone",7,Material.Bone.Copy(),null,this){MainBodyPart = LegsBodyPart};
		Organ LeftLeg = new Organ("Left Leg",7,Material.Flesh.Copy(),LeftLegBone,this){MainBodyPart = LegsBodyPart};
		Organ RightLegBone = new Organ("Right Leg Bone",7,Material.Bone.Copy(),null,this){MainBodyPart = LegsBodyPart};
		Organ RightLeg = new Organ("Right Leg",7,Material.Flesh.Copy(),RightLegBone,this){MainBodyPart = LegsBodyPart};
		
		Organ LeftArmBone = new Organ("Left Arm Bone",7,Material.Bone.Copy(),null,this){MainBodyPart = ArmsBodyPart};
		Organ LeftArm = new Organ("Left Arm",7,Material.Flesh.Copy(),LeftArmBone,this){MainBodyPart = ArmsBodyPart};
		Organ RightArmBone = new Organ("Right Arm Bone",7,Material.Bone.Copy(),null,this){MainBodyPart = ArmsBodyPart};
		Organ RightArm = new Organ("Right Arm",7,Material.Flesh.Copy(),RightArmBone,this){MainBodyPart = ArmsBodyPart};
		
		HeadBodyPart.ExposedOrgans.Add(Head);
		TorsoBodyPart.ExposedOrgans.Add(Torso);
		ArmsBodyPart.ExposedOrgans.AddRange(new Organ[]{LeftArm,RightArm});
		LegsBodyPart.ExposedOrgans.AddRange(new Organ[]{LeftLeg,RightLeg});
		
		foreach(BodyPart BdyPart in AllBodyParts.Values){
			foreach(Organ Org in BdyPart.ExposedOrgans){
				AllOrgans.Add(Org.Id, Org);
				Org.Health = Org.Health * (Host.GetModifiers().MaxHealth / 100);
				Org.InitialHealth = Org.InitialHealth * (Host.GetModifiers().MaxHealth / 100);
				foreach(Organ ChildOrg in GetAllChildOrgans(Org)){
					AllOrgans.Add(ChildOrg.Id, ChildOrg);
					ChildOrg.Health = ChildOrg.Health * (Host.GetModifiers().MaxHealth / 100);
					ChildOrg.InitialHealth = ChildOrg.InitialHealth * (Host.GetModifiers().MaxHealth / 100);
				}
			}
		}
	}
	//}
	//Blood system
	public float Blood{get{return Host.GetHealth();}set{
		if(Host.GetHealth() > value)
			Host.DealDamage(Math.Abs(Host.GetHealth() - value));
		else if(!Host.IsStrengthBoostActive)
			Host.SetHealth(value);
		
	}}
	public float MaxBlood = 100;
	public float BleedingPorcentage = 0;
	public int BleedingIntervalMS = 0;
	
	public bool IsBleedingStagnation = false;
	
	public void CheckBleeding(){
		UpdateBleedingInterval();
		if(IsBleedingStagnation)return;
		
		Blood-=3;
		BleedingPorcentage -= BleedingPorcentage - 3 < 0 ? BleedingPorcentage : 3;
		
		if(!DynamicEffects || FramesPerSecond < 50)return;
		int i = 0;
		for(; i < Math.Min(13,BleedingPorcentage/3); i++){
			new BloodNode(Host.GetWorldPosition() + new Vector2(0,6),new Vector2(rnd.Next(-5,5),rnd.Next(2,8)) + Host.GetLinearVelocity());
		}
		Game.WriteToConsole("Blood Node Spawned: "+i.ToString()+". Position: "+(Host.GetWorldPosition() + new Vector2(0,6)).ToString()+".");
	}
	public void UpdateBleedingInterval(){
		if(BleedingPorcentage == 0){
			BleedingIntervalMS = 0;
			return;
		}
		BleedingIntervalMS = (int)Math.Max( 200 ,Math.Min( 500 ,(200 / BleedingPorcentage) * 200 ));
		BleedingIntervalMS *= (int)(1 * Game.SlowmotionModifier);
	}
	public void CheckBloodRegeneration(){
		if((Host.IsCrouching || Host.IsTakingCover) && Host.IsWalking && BleedingPorcentage > 0){
			IsBleedingStagnation = true;
			BleedingPorcentage -= BleedingPorcentage - 8 < 0 ? BleedingPorcentage : 8;
			Game.PlayEffect("CFTXT", new Vector2(Host.GetWorldPosition().X, Host.GetWorldPosition().Y + 10), "Bleeding Porcentage: "+((int)BleedingPorcentage).ToString()+"%");
			UpdateBleedingInterval();
		}
		else{
			IsBleedingStagnation = false;
			if(BleedingPorcentage <= 0)
				Blood= Blood + 2 > MaxBlood ? MaxBlood : Blood + 2;
		}
	}
	public void CheckMaxBlood(){
		float TotalHead = (GetAllHealth(AllBodyParts["Head"].ExposedOrgans) / GetAllInitialHealth(AllBodyParts["Head"].ExposedOrgans)) * (Host.GetModifiers().MaxHealth); 
		float TotalTorso = (GetAllHealth(AllBodyParts["Torso"].ExposedOrgans) / GetAllInitialHealth(AllBodyParts["Torso"].ExposedOrgans)) * (Host.GetModifiers().MaxHealth);
		MaxBlood = Math.Min(TotalHead,TotalTorso);
		Blood=  Blood > MaxBlood ? MaxBlood : Blood;
	}
	
	//}
	
	//BodyParts, that store some organs in a specific area of the body and some management methods
	public Dictionary<string, Organ> AllOrgans = new Dictionary<string, Organ>();
	public Dictionary<string, BodyPart> AllBodyParts = new Dictionary<string, BodyPart>();
	
	public BodyPart GetBodyPartByLocal(object Arg, bool Filter){
		List<BodyPart> Result = new List<BodyPart>();
		if(Arg is Vector2)Result = AllBodyParts.Values.Where(Part => Part.GetRelativeBodyPartArea().Contains((Vector2)Arg)).ToList();
		else if(Arg is Area)Result = AllBodyParts.Values.Where(Part => Part.GetRelativeBodyPartArea().Intersects((Area)Arg)).ToList();
		if(Arg == null)Result = AllBodyParts.Values.ToList();

		if(Filter)
			Result = Result.Where(Part => GetAllHealth(Part.ExposedOrgans) > 0).ToList().Count > 0 ? Result.Where(Part => GetAllHealth(Part.ExposedOrgans) > 0).ToList() : Result;
		
		List<BodyPart> HitChanceResult = new List<BodyPart>();
		for(int i = 0;i < Result.Count; i++){
			if(rnd.NextDouble() <= Result[i].HitChance)
				HitChanceResult.Add(Result[i]);
		}
		if(HitChanceResult.Count > 0)Result = HitChanceResult;
		
		return Result.ToArray().Length > 0 ? Result[rnd.Next(Result.ToArray().Length)] : null;
	}
	public class BodyPart{
		public string Id = "";
		public double HitChance = 1.0;
		
		public BodyProperties Parent;
		
		public Area BodyPartArea;
		public Area GetRelativeBodyPartArea(){return Parent.RelativeBodyArea(BodyPartArea);}
		
		public Vector2 BodyPartPosition;
		public Vector2 GetEffectPosition(){return (BodyPartPosition + Parent.Host.GetWorldPosition()) + new Vector2(rnd.Next(-3,3),rnd.Next(-3,3));}
		public List<Organ> ExposedOrgans = new List<Organ>();
		public Armour Arm = null;
		
		public BodyPart(BodyProperties ParentP, Area BodyPartAreaP, string IdP, Vector2 BodyPartPositionP){
			Parent = ParentP;
			BodyPartArea = BodyPartAreaP;
			Id = IdP;
			BodyPartPosition = BodyPartPositionP;
			
			Parent.AllBodyParts.Add(Id, this);
		}
		
		public void DefaultDamage(DamageData Args){
			Game.WriteToConsole(Parent.Host.Name+" Damaged on "+Id+". Damage: "+Args.GetTotalDamage()+".");
			if(Parent.Host.IsRemoved){
				BodyPropertiesDictionary.Remove(Parent.Host);
				return;
			}
			if(Id == "Legs")Parent.SpeedModifierCheck();
			
			if(Arm != null){
				Arm.Org.InduceDamage(ref Args);
				if(Arm.Org.Destroyed)Arm.RemoveArmour();
			}
			
			if(Args.GetTotalDamage() > 0 && Args.PenetrationFactor != 0){
				Parent.BleedingPorcentage += Args.BleedingFactor/1.5f;
				Parent.Blood -= Args.BleedingFactor/3;
				Parent.UpdateBleedingInterval();
				
				List<Organ> FExposedOrgans = HealthFilter(ExposedOrgans);
				if(FExposedOrgans.Count > 0)FExposedOrgans[rnd.Next(FExposedOrgans.Count)].InduceDamage(ref Args);
			}
			
			if(FramesPerSecond < 50)return;
			if(Parent.DamageTexts.ContainsKey(Id)){
				Parent.DamageTexts[Id].Interrupt();
				Parent.DamageTexts.Remove(Id);
			}
			if(DynamicEffects)
				Parent.DamageTexts.Add(Id,DynamicText.DamageText(Id,50,Args.HitPosition,Args.HitImpulse,new Vector2(0),this,Args));
			else{
				Parent.DamageTexts.Add(Id,new StaticText(Id,50,Args.HitPosition));
				Parent.DamageTexts[Id].SetColorScale(GetAllHealth(ExposedOrgans) / GetAllInitialHealth(ExposedOrgans),Arm != null ? Arm.Org.Health / Arm.Org.InitialHealth : 0);
			}
			Parent.DamageTexts[Id].Start();
		}
	}
	//}
	
	//Death System
	public Events.UpdateCallback InvincibilityDeathChecker = null;
	
	public void CheckDeath(){
		if(Host.IsStrengthBoostActive){
			if(InvincibilityDeathChecker == null){
				InvincibilityDeathChecker = Events.UpdateCallback.Start((e) =>{
					if(!Host.IsStrengthBoostActive){
						CheckDeath();
						InvincibilityDeathChecker.Stop();
						InvincibilityDeathChecker = null;
					}
				},100,0);
			}
			return;
		}
		
		Organ[] VitalOrgans = new Organ[]{AllOrgans["Brain"],AllOrgans["Hearth"]};
		if(Host.IsDead)return;
		
		bool Death = false;
		foreach(Organ VitalOrgan in VitalOrgans){
			if(VitalOrgan.Destroyed)
				Death = true;
		}
		if(Death)InduceDeath();
	}
	public void InduceDeath(){
		if(Host.IsFalling || Host.IsInMidAir){
			Host.Kill();
			return;
		}
		Host.ClearCommandQueue();
		Host.SetInputEnabled(false);
		Host.SetStatusBarsVisible(false);
		Host.SetNametagVisible(false);
		Host.SetHealth(1f);
		Host.AddCommand(new PlayerCommand(PlayerCommandType.DeathKneel));
		Events.UpdateCallback.Start((float r) => {
			if(!Host.IsDeathKneeling && !Host.IsDead)Host.Kill();
		},50,30);
	}
	//}
	
	//Some tools used by other systems
	public void OnOrganDestroyed(Organ Org, DamageData DmgData){
		RunEffect("Broken",Org.MainBodyPart.GetEffectPosition(),DmgData.GetTotalDamage());
		Game.PlayEffect("CFTXT", Org.MainBodyPart.GetEffectPosition(), Org.Id+" Was Destroyed");
		CheckDeath();
		
		if(Org.MainMaterial.MaterialDebris != null){
			string[] MaterialDebris = Org.MainMaterial.MaterialDebris;
			for(int i = 0;i < Org.InitialHealth/5;i++){
				IObject obj = (IObject)Game.CreateObject(Org.MainMaterial.MaterialDebris[rnd.Next(Org.MainMaterial.MaterialDebris.Length)],Org.MainBodyPart.GetEffectPosition());
				if(DmgData.HitArguments is IProjectile)obj.SetLinearVelocity(((IProjectile)DmgData.HitArguments).Velocity/500);
			}
		}
	}
	public static void RunEffect(string EffectId, Vector2 Position, float Force){
		if(Force > 1 && GlobalMultipliers["CSM"] > 0)Game.PlayEffect("CAM_S", Position, Math.Min(Force/1.8f,70) * GlobalMultipliers["CSM"], 100f, false);
		Position = Position + new Vector2(rnd.Next(-3,3),rnd.Next(-3,3));
		switch(EffectId){
			case "BrokenSound":{
				Game.PlaySound("BalloonPop", Position, Force * 1.2f);
				Game.PlaySound("Baseball", Position, Force * 1.2f);
				Game.PlaySound("DestroySmall", Position, Force / 7f);
				Game.PlaySound("BulletHitPaper", Position, Force / 5f);
				break;
			}
			case "Broken":{
				RunEffect("BrokenSound",Position,Force);
				Game.PlayEffect("Smack", Position);
				break;
			}
			case "Bone":{
				Game.PlayEffect("Smack", Position);
				break;
			}
			case "Flesh":{
				if(Force > 4){
					Game.PlaySound("ImpactFlesh", Position, Force * 1.2f);
					Game.PlayEffect("BLD", Position);
				}
				else
					Game.PlayEffect("TR_B", Position);
				break;
			}
			case "Cloth":{
				if(Force > 3){
					Game.PlayEffect("DestroyCloth", Position);
					Game.PlayEffect("DestroyCloth", Position);
				}
				for(int i = 0;i <= Math.Min(Force,10);i++){
					Game.PlayEffect("F_S", Position);
				}
				Game.PlayEffect("Block", Position);
				Game.PlayEffect("Smack", Position);
				break;
			}
			case "Steel":{

				for(int i = 0;i <= Math.Min(Force/2,5);i++){
					Game.PlaySound("ImpactMetal", Position);
					Game.PlayEffect("Smack", Position);
					if(Force > 2)Game.PlayEffect("DestroyMetal", Position);
				}
				break;
			}
			case "BulletProof":{
				RunEffect("Cloth",Position,Force/2);
				RunEffect("Steel",Position,Force/2);
				break;
			}
		}
	}
	public virtual Area RelativeBodyArea(Area PArea){
		Area ReturnArea = PArea;
		ReturnArea.TopRight+=Host.GetWorldPosition();
		ReturnArea.BottomLeft+=Host.GetWorldPosition();
		if(Host.IsCrouching){
			ReturnArea.TopRight -= new Vector2(0,5);
			ReturnArea.BottomLeft -= new Vector2(0,5);
		}
		return ReturnArea;
	}
	
	public Area PunchArea(){
		Area ThisArea = Host.IsInMidAir ? 
			new Area(16,0,6,60) : new Area(10,-10,6,60);
		if(Host.FacingDirection == -1){
			float x = ThisArea.Right;
			ThisArea.Right = ThisArea.Left * -1;
			ThisArea.Left = x * -1;
		}
		return RelativeBodyArea(ThisArea);
	}
	
	public void SpeedModifierCheck(){
		PlayerModifiers HostMod = Host.GetModifiers();
		float LegsHealth = Math.Min(Math.Max(GetAllHealth(AllBodyParts["Legs"].ExposedOrgans) / GetAllInitialHealth(AllBodyParts["Legs"].ExposedOrgans),0.5f),1f);
		
		HostMod.RunSpeedModifier = OriginalPlayerModifiers.RunSpeedModifier;
		HostMod.RunSpeedModifier = (HostMod.RunSpeedModifier * LegsHealth);
		HostMod.RunSpeedModifier = Math.Min((HostMod.RunSpeedModifier / GlobalMultipliers["LSM"]),OriginalPlayerModifiers.RunSpeedModifier);
		
		HostMod.SprintSpeedModifier = OriginalPlayerModifiers.SprintSpeedModifier;
		HostMod.SprintSpeedModifier = (HostMod.SprintSpeedModifier * LegsHealth);
		HostMod.SprintSpeedModifier = Math.Min((HostMod.SprintSpeedModifier / GlobalMultipliers["LSM"]),OriginalPlayerModifiers.SprintSpeedModifier);
		
		Host.SetModifiers(HostMod);
	}
	public float MeleeForceMultiplier(){
		string RegionIndex = Host.IsKicking ? "Legs" : "Arms";
		float CurrentHP = GetAllHealth(HealthFilter(AllBodyParts[RegionIndex].ExposedOrgans));
		float OriginalHP = GetAllInitialHealth(HealthFilter(AllBodyParts[RegionIndex].ExposedOrgans));
		return (CurrentHP / OriginalHP) * Host.GetModifiers().MeleeDamageDealtModifier;
	}
	public Dictionary<string,StaticText> DamageTexts = new Dictionary<string,StaticText>();
	//}
}

public class StaticText{
	public IObjectText TextObj;
	public int TotalTicks = 0;
	public int MaxTicks = 0;
	
	public Events.UpdateCallback TickCaller = null;
	
	public StaticText(string Text, int MaxTicksP, Vector2 Position){
		MaxTicks = MaxTicksP;

		TextObj = (IObjectText)Game.CreateObject("Text",Position);
		TextObj.SetTextScale(0.8f);
		TextObj.SetText(Text);
		TextObj.SetTextAlignment(TextAlignment.Middle);
		
		Tick=(float e)=>{
			TotalTicks++;
			InterruptCondition();
		};
		InterruptCondition=()=>{
			if(TotalTicks >= MaxTicks && MaxTicks > 0)Interrupt();
		};
	}
	public virtual void Start(){
		Interrupt+=()=>{
			TextObj.Remove();
			TickCaller.Stop();
		};
		TickCaller = Events.UpdateCallback.Start(Tick, 50, (ushort)MaxTicks);
		Tick(0);
	}
	
	public void SetColorScale(float GreenScale, float BlueScale){
		GreenScale *= 255;
		BlueScale *= 255;
		GreenScale = Math.Max(Math.Min(GreenScale,255),0);
		BlueScale = Math.Max(Math.Min(BlueScale,255),0);
		TextObj.SetTextColor(new Color(Convert.ToByte(255 - GreenScale), Convert.ToByte(GreenScale),Convert.ToByte(BlueScale)));
	}
	
	public Action<float> Tick = (e)=>{};
	public Action InterruptCondition = ()=>{};
	public Action Interrupt = ()=>{};
	
	public static StaticText WeldText(string Text, int MaxTicksP, IObject Object, Vector2 PositionDiference){
		StaticText Return = new StaticText(Text,MaxTicksP,Object.GetWorldPosition());
		Return.Tick += (float e)=>{
			Return.TextObj.SetWorldPosition(Object.GetWorldPosition() + PositionDiference);
		};
		Return.InterruptCondition += ()=>{
			if(Object.IsRemoved)Return.Interrupt();
		};
		return Return;
	}
	public static StaticText BdyPartText(BodyProperties.BodyPart BdyPart, Vector2 PositionDiference){
		StaticText Return = StaticText.WeldText(BdyPart.Id,0,BdyPart.Parent.Host,PositionDiference);
		
		Return.Tick += (float e)=>{
			float GreenScale = GetAllHealth(BdyPart.ExposedOrgans) / GetAllInitialHealth(BdyPart.ExposedOrgans);
			float BlueScale = BdyPart.Arm != null ? (BdyPart.Arm.Org.Health / BdyPart.Arm.Org.InitialHealth) : 0;
			if(GreenScale * 255 < 1)
				Return.TextObj.SetTextColor(new Color(255,255,255));
			else
				Return.SetColorScale(GreenScale,BlueScale);
		};
		Return.InterruptCondition += ()=>{
			if(!BdyPart.Parent.Host.IsWalking)Return.Interrupt();
		};
		Return.Interrupt += ()=>{
			BdyPart.Parent.OrgansText.Clear();
		};

		return Return;
	}
}

public class DynamicText : StaticText{
	public Vector2 Velocity = new Vector2(0);
	public Vector2 GravityVel = new Vector2(0);
	public DynamicText(string Text, int MaxTicksP, Vector2 Position, Vector2 VelocityP, Vector2 GravityVelP) : base(Text,MaxTicksP,Position){
		Velocity = VelocityP;
		GravityVel = GravityVelP;
		InterruptCondition += ()=>{
			if(FramesPerSecond < 35)Interrupt();
		};
		Tick += (float e)=>{
			Velocity.X = Velocity.X < 0 ? 
			(Velocity.X - -0.25f > 0 ? 0 : Velocity.X - -0.25f) : 
			(Velocity.X + -0.25f < 0 ? 0 : Velocity.X + -0.25f);
			
			Velocity.Y = Velocity.Y < 0 ? 
			(Velocity.Y - -0.25f > 0 ? 0 : Velocity.Y - -0.25f) : 
			(Velocity.Y + -0.25f < 0 ? 0 : Velocity.Y + -0.25f);
			TextObj.SetWorldPosition(TextObj.GetWorldPosition() + Velocity);
		};
	}
	public static DynamicText DamageText(string Text, int MaxTicksP, Vector2 Position, Vector2 VelocityP, Vector2 GravityVelP, BodyProperties.BodyPart BdyPart, DamageData DmgData){
		DynamicText Return = new DynamicText(Text,MaxTicksP,Position,VelocityP,GravityVelP);
		float GreenScale = GetAllHealth(BdyPart.ExposedOrgans) / GetAllInitialHealth(BdyPart.ExposedOrgans);
		float BlueScale = BdyPart.Arm != null ? (BdyPart.Arm.Org.Health / BdyPart.Arm.Org.InitialHealth) : 0;
		if(GreenScale * 255 < 1){
			Return.TextObj.SetTextColor(new Color(255,255,255));
			Return.Tick += (float e)=>{
				Color color = Return.TextObj.GetTextColor();
				color.R = Convert.ToByte(color.R - 15 < 0 ? 0 : color.R - 15);
				color.G = color.R;
				color.B = color.R;
				
				Return.TextObj.SetTextColor(color);
			};
		}
		else
			Return.SetColorScale(GreenScale,BlueScale);
		
		Return.TextObj.SetTextScale(Math.Max(Math.Min(1.3f,DmgData.GetTotalDamage()/10),0.4f));
		return Return;
	}
}

public class BloodNode{
	public IObject BloodParticle;
	public bool WallSliding = false;
	public int Ticks = 0;
	public static int MaxTicks = 60;
	public void BloodTick(float e){
		Ticks++;
		if(BloodParticle.IsRemoved)return;
		
		Vector2 VelInfluence = new Vector2(0);
		var WillSlide = false;
		var WillRemoved = false;
		foreach(IObject obj in Game.GetObjects(BloodParticle.GetAABB())){
			if(obj.GetCollisionFilter().MaskBits != 0){
				if(obj.GetBodyType() == BodyType.Dynamic){
					VelInfluence = (VelInfluence.X + VelInfluence.Y) < (obj.GetLinearVelocity().X + obj.GetLinearVelocity().Y) ? obj.GetLinearVelocity() : VelInfluence;
					WillSlide = true;
				}
				else
					if(obj.GetBodyType() == BodyType.Static)
						WillRemoved = true;
			}
			else 
				if(obj.Name.Contains("Bg") && obj.Name != "BgWall00E" && WallSliding)WillSlide = true;
			
			
		}
		if(WillSlide){
			BloodParticle.SetLinearVelocity((BloodParticle.GetLinearVelocity() / 1.5f) + (VelInfluence / 2));
		}
		
		if(WillRemoved || Ticks == MaxTicks){
			BloodParticle.Remove();
		}
	}
	
	public BloodNode(Vector2 Position, Vector2 LinearVelocity){
		BloodParticle = (IObject)Game.CreateObject("BgWall00E",Position);
		BloodParticle.SetBodyType(BodyType.Dynamic);
		BloodParticle.SetLinearVelocity(LinearVelocity);
		BloodParticle.SetAngularVelocity(0);
		BloodParticle.SetColor1("BgRed");
		Events.UpdateCallback.Start(BloodTick, 100, (ushort)MaxTicks);
		WallSliding = rnd.Next(0,10) > 8;
	}
}

public class Organ{
	public string Id = "";
	public float Health = 0f;
	public float InitialHealth = 0f;
	public bool Destroyed{get{return Health <= 0;}set{Health = Convert.ToInt32(value);}}
	public Organ ChildOrgan; //Next Organ to be damaged
	
	public Material MainMaterial;
	
	public BodyProperties.BodyPart MainBodyPart;
	public BodyProperties MainBodyProperties;
	
	public Organ(string IdP, float HealthP, Material MainMaterialP, Organ ChildOrganP, BodyProperties MainBodyPropertiesP){
		Id = IdP;
		Health = HealthP;
		InitialHealth = Health;
		MainMaterial = MainMaterialP;
		ChildOrgan = ChildOrganP;
		MainBodyProperties = MainBodyPropertiesP;
	}
	public void InduceDamage(ref DamageData Args){
		if(!Destroyed){
			BodyProperties.RunEffect(MainMaterial.Id,Args.HitPosition != new Vector2(0) ? Args.HitPosition : MainBodyPart.GetEffectPosition()
				, Args.BluntDamage != 0 ? (Args.BluntDamage * (float)rnd.NextDouble()) : 0);
			MainMaterial.ApplyResistence(ref Args);
			
			float HealthTemp = Health / (Args.PenetrationFactor * 3);
			Health -= Args.GetTotalDamage() / Args.PenetrationFactor;
			
			Args.SharpDamage -= Args.SharpDamage - HealthTemp < 0 ? Args.SharpDamage : HealthTemp;
			Args.BluntDamage -= Args.BluntDamage - HealthTemp < 0 ? Args.BluntDamage : HealthTemp;
			Args.ThermalDamage -= Args.ThermalDamage - HealthTemp < 0 ? Args.ThermalDamage : HealthTemp;
			
			if(Destroyed && MainBodyProperties != null){MainBodyProperties.OnOrganDestroyed(this, Args);}
			
			Args.PenetrationFactor -= Args.PenetrationFactor - MainMaterial.PenetrationResistence < 0 ? Args.PenetrationFactor : MainMaterial.PenetrationResistence;
		}
		if(ChildOrgan != null && Args.GetTotalDamage() > 0 && Args.PenetrationFactor > 0)
			ChildOrgan.InduceDamage(ref Args);
	}
}

public class Material{
	public static Material Flesh = new Material("Flesh",1f,0.80f,0.80f,1);
	public static Material Bone = new Material("Bone",0.80f,1f,0.60f,1);
	
	public static Material LightSteel = new Material("Steel",0.75f,0.60f,0.30f,1){MaterialDebris = new string[]{"MetalDebris00B","MetalDebris00C","MetalDebris00D"}};
	public static Material HeavySteel = new Material("Steel",0.60f,0.45f,0.15f,2){MaterialDebris = new string[]{"ItemDebrisDark00","ItemDebrisDark01","ItemDebrisShiny01"}};
	
	public static Material LightCloth = new Material("Cloth",0.90f,0.90f,0.95f,0);
	public static Material HeavyCloth = new Material("Cloth",0.75f,0.75f,0.75f,1);
	
	public static Material BulletProof = new Material("BulletProof",0.50f,0.30f,0.10f,2);
	
	public string Id = "";
	public float SharpMultiplier = 1f;
	public float BluntMultiplier = 1f;
	public float ThermalMultiplier = 1f;
	
	public int PenetrationResistence = 1;
	
	public string[] MaterialDebris = null;
	
	public Material(string IdP, float SharpMultiplierP, float BluntMultiplierP, float ThermalMultiplierP, int PenetrationResistenceP){
		Id = IdP;
		SharpMultiplier = SharpMultiplierP;
		BluntMultiplier = BluntMultiplierP;
		ThermalMultiplier = ThermalMultiplierP;
		PenetrationResistence = PenetrationResistenceP;
	}
	public void ApplyResistence(ref DamageData Args){
		Args.SharpDamage *= SharpMultiplier;
		Args.BluntDamage *= BluntMultiplier;
		Args.ThermalDamage *= ThermalMultiplier;
	}
	public Material Copy(){
		return (Material)this.MemberwiseClone();
	}
}

public static Dictionary<IObjectActivateTrigger,Armour> GetArmourByObj = new Dictionary<IObjectActivateTrigger,Armour>();

public class Armour{
	public Organ Org;
	public IObject Object;
	public StaticText Text;
	
	public string[] BodyPartsNames = null;
	public BodyProperties.BodyPart[] BodyParts = null;
	
	public IProfileClothingItem[] VisualCloth;
	public string[] VisualClothPartName;
	
	public void ApplyArmour(BodyProperties BdyProp){
		BodyParts = BdyProp.AllBodyParts.Values.Where(BdyPart => BodyPartsNames.ToList().Contains(BdyPart.Id)).ToArray();
		foreach(BodyProperties.BodyPart BdyPart in BodyParts){
			if(BdyPart.Arm != null)BdyPart.Arm.RemoveArmour();
			BdyPart.Arm = this;
		}
		Org.MainBodyProperties = BdyProp;
		Org.MainBodyPart = BodyParts[rnd.Next(BodyParts.Length)];
		
		BdyProp.UpdateIProfile();
	}
	public void RemoveArmour(){
		foreach(BodyProperties.BodyPart BdyPart in BodyParts){
			BdyPart.Arm = null;
		}
		Org.MainBodyPart = null;
		Org.MainBodyProperties.UpdateIProfile();
		
		if(Org.Health > 0)
			SpawnArmourObject(this,Org.MainBodyProperties.Host.GetWorldPosition());
		
		Org.MainBodyProperties = null;
	}
	
	public static Armour Mask(){return new Armour{
			Org = new Organ("Mask",7,Material.LightSteel.Copy(),null,null),
			BodyPartsNames = new string[]{"Head"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("WeldingHelmet","ClothingLightGray","","")},
			VisualClothPartName = new string[]{"Head"}
	};}
	public static Armour LHelmet(){return new Armour{
			Org = new Organ("L. Helmet",8,Material.LightSteel.Copy(),null,null),
			BodyPartsNames = new string[]{"Head"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("Helmet","ClothingDarkGray","","")},
			VisualClothPartName = new string[]{"Head"}
	};}
	public static Armour HeHelmet(){return new Armour{
			Org = new Organ("He. Helmet",8,Material.HeavySteel.Copy(),null,null),
			BodyPartsNames = new string[]{"Head"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("Helmet2","ClothingDarkGray","","")},
			VisualClothPartName = new string[]{"Head"}
	};}
	
	public static Armour CthVest(){return new Armour{
			Org = new Organ("Cth. Vest",10,Material.HeavyCloth.Copy(),null,null),
			BodyPartsNames = new string[]{"Torso","Arms","Legs"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("Poncho","ClothingDarkGray","ClothingDarkGray",""),
			new IProfileClothingItem("TrainingShirt","ClothingDarkGray","",""),
			new IProfileClothingItem("GlovesBlack","ClothingDarkGray","",""),
			new IProfileClothingItem("Pants","ClothingDarkGray","",""),
			new IProfileClothingItem("Sash","ClothingDarkGray","","")
			},
			VisualClothPartName = new string[]{"ChestOver","ChestUnder","Hands","Legs","Waist"}
	};}
	public static Armour StlVest(){return new Armour{
			Org = new Organ("Stl. Vest",10,Material.HeavySteel.Copy(),null,null),
			BodyPartsNames = new string[]{"Torso","Arms","Legs"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("BodyArmor","ClothingDarkGray","",""),
			new IProfileClothingItem("KevlarVest","","",""),
			new IProfileClothingItem("Gloves","ClothingDarkGray","",""),
			new IProfileClothingItem("StripedPants","ClothingDarkGray","",""),
			new IProfileClothingItem("CombatBelt","ClothingDarkGray","","")
			},
			VisualClothPartName = new string[]{"ChestUnder","ChestOver","Hands","Legs","Waist"}
	};}
	public static Armour MLawVest(){return new Armour{
			Org = new Organ("MLaw. Vest",13,Material.HeavySteel.Copy(),null,null),
			BodyPartsNames = new string[]{"Torso","Arms","Legs"},
			VisualCloth = new IProfileClothingItem[]{new IProfileClothingItem("MetroLawJacket","ClothingDarkGray","ClothingDarkGray",""),
			new IProfileClothingItem("GlovesBlack","ClothingDarkGray","",""),
			new IProfileClothingItem("PantsBlack","ClothingDarkGray","",""),
			new IProfileClothingItem("Balaclava","ClothingDarkGray","",""),
			new IProfileClothingItem("Belt","ClothingDarkGray","ClothingDarkGray","")
			},
			VisualClothPartName = new string[]{"ChestOver","Hands","Legs","Accessory","Waist"}
	};}
		
	public static Armour GetArmour(int Index){
		Armour[] Armours = new Armour[]{
			HeHelmet(),MLawVest(),StlVest(),LHelmet(),Mask()
		};
		if(Index == -1 || Index >= Armours.Length)Index = rnd.Next(Armours.Length);
		
		Armour SelectedArmour = Armours[Index];
		SelectedArmour.Org.InitialHealth *= GlobalMultipliers["AHM"];
		SelectedArmour.Org.Health = SelectedArmour.Org.InitialHealth;
		return SelectedArmour;
	}
	public Armour Copy(){
		return (Armour)this.MemberwiseClone();
	}
}

public static void SpawnArmourObject(Armour Arm, Vector2 Pos){
	IObject ArmourObject = (IObject)Game.CreateObject("Helmet00", Pos);
	IObjectActivateTrigger ActivateTrigger = (IObjectActivateTrigger)Game.CreateObject("ActivateTrigger", Pos);
	ActivateTrigger.SetBodyType(BodyType.Dynamic);
	ActivateTrigger.SetHighlightObject(ArmourObject);
	ActivateTrigger.CustomId = ArmourObject.UniqueID.ToString();
	
	IObjectWeldJoint WeldJoint = (IObjectWeldJoint)Game.CreateObject("WeldJoint");
	ArmourObject.CustomId = WeldJoint.UniqueID.ToString();

	WeldJoint.AddTargetObject(ArmourObject);
	WeldJoint.AddTargetObject(ActivateTrigger);
	
	GetArmourByObj.Add(ActivateTrigger,Arm);
	
	Arm.Object = ArmourObject;
	Arm.Text = StaticText.WeldText(Arm.Org.Id,0,ArmourObject,new Vector2(0,5));
	Arm.Text.SetColorScale(0,Arm.Org.Health / Arm.Org.InitialHealth); 
	Arm.Text.Start();
	ActivateTrigger.SetScriptMethod("ArmourFunction");
}

public void ArmourFunction(TriggerArgs Args){
	IPlayer Ply;
	try{Ply = (IPlayer)Args.Sender;}catch(Exception){return;}
	BodyProperties BdyProp = GetBodyPropertiesByIPlayer(Ply);
	
	IObjectActivateTrigger ActivateTrigger = (IObjectActivateTrigger)Args.Caller;
	Armour Arm = GetArmourByObj[ActivateTrigger];
	IObject ArmourObject = Arm.Object;
	IObjectWeldJoint WeldJoint = (IObjectWeldJoint)Game.GetObject(Int32.Parse(ArmourObject.CustomId));
	
	Arm.ApplyArmour(BdyProp);
	
	ActivateTrigger.Remove();
	ArmourObject.Remove();
	WeldJoint.Remove();
	Game.PlayEffect("CFTXT", Ply.GetWorldPosition() + new Vector2(0,6),Arm.Org.Id);
	Game.PlaySound("GetHealthLarge",Ply.GetWorldPosition());
}

public void OnObjectCreated(IObject[] objs){
	foreach(IObject obj in objs){
		if(obj.Name == "ItemPills" || (obj.Name == "ItemMedkit" && rnd.Next(5) == 0)){
			SpawnArmourObject(Armour.GetArmour(-1),obj.GetWorldPosition());
			obj.Destroy();
		}
	}
}

public class DamageData{
	public float SharpDamage = 0f;
	public float BluntDamage = 0f;
	public float ThermalDamage = 0f;
	
	public float BleedingFactor = 0f;
	public float PenetrationFactor = 0f;
	
	public object HitArguments = null;
	public Vector2 HitPosition = new Vector2(0);
	public Vector2 HitImpulse = new Vector2(0);
	public DamageData(float SharpDamageP, float BluntDamageP, float ThermalDamageP, float BleedingFactorP, float PenetrationFactorP){
		SharpDamage = SharpDamageP;
		BluntDamage = BluntDamageP;
		ThermalDamage = ThermalDamageP;
		BleedingFactor = BleedingFactorP * GlobalMultipliers["BLEM"];
		PenetrationFactor = PenetrationFactorP;
	}
	public float GetTotalDamage(){
		return SharpDamage + (BluntDamage + ThermalDamage);
	}
	public DamageData Copy(){
		return (DamageData)this.MemberwiseClone();
	}
}

public static int FramesPerSecond = 0;
public static Dictionary<IPlayer,BodyProperties> BodyPropertiesDictionary = new Dictionary<IPlayer,BodyProperties>();
public BodyProperties GetBodyPropertiesByIPlayer(IPlayer Ply){
	if(!BodyPropertiesDictionary.ContainsKey(Ply)){
		BodyPropertiesDictionary.Add(Ply, new BodyProperties(Ply));
	}
	return BodyPropertiesDictionary[Ply];
}

public static Dictionary<string,DamageData> GetProjectileDamageData = new Dictionary<string,DamageData>();
public static Dictionary<string,DamageData> GetMeleeDamageData = new Dictionary<string,DamageData>();
public void AfterStartup(){
}
public void OnStartup(){
	AddMultipliers();
	
	if(Game.LocalStorage.ContainsKey("DYNAMIC_EFFECTS_HD"))
	DynamicEffects = (bool)Game.LocalStorage.GetItem("DYNAMIC_EFFECTS_HD");
	
	foreach(IPlayer Ply in Game.GetPlayers()){
		BodyPropertiesDictionary.Add(Ply, new BodyProperties(Ply));
	}
	
	Events.UpdateCallback.Start((float e) => {
		foreach(BodyProperties BdyProp in BodyPropertiesDictionary.Values.Where(BdyProp => !BdyProp.Host.IsDead)){
			BdyProp.CheckWasFalling();
			
			if(BdyProp.BleedingIntervalMS > 0){
				BdyProp.BleedingIntervalMS -= 200;
				if(BdyProp.BleedingIntervalMS <= 0)
					BdyProp.CheckBleeding();
			}
			
			if(BdyProp.Host.IsWalking && BdyProp.OrgansText.Count < 1){
				BdyProp.OrgansText.Add(StaticText.BdyPartText(BdyProp.AllBodyParts["Head"],new Vector2(-10,11)));
				BdyProp.OrgansText.Add(StaticText.BdyPartText(BdyProp.AllBodyParts["Torso"],new Vector2(-10,3)));
				BdyProp.OrgansText.Add(StaticText.BdyPartText(BdyProp.AllBodyParts["Arms"],new Vector2(8,11)));
				BdyProp.OrgansText.Add(StaticText.BdyPartText(BdyProp.AllBodyParts["Legs"],new Vector2(8,3)));
				foreach(StaticText Txt in BdyProp.OrgansText){
					Txt.TextObj.SetTextScale(Txt.TextObj.GetTextScale() / 1.5f);
					Txt.Start();
				}
			}
		}
	},250,0);
	Events.UpdateCallback.Start((float e) => {
		foreach(BodyProperties BdyProp in BodyPropertiesDictionary.Values.Where(BdyProp => !BdyProp.Host.IsDead)){
			BdyProp.CheckBloodRegeneration();
		}
	},800,0);
	
	int FramesPerSecondCount = 0;
	Events.UpdateCallback.Start((float e) => {
		FramesPerSecondCount++;
	},0,0);
	Events.UpdateCallback.Start((float e) => {
		FramesPerSecond = FramesPerSecondCount;
		FramesPerSecondCount=0;
	},1000,0);
	
	m_projectileHitEvent = Events.ProjectileHitCallback.Start(OnProjectileHit);
	m_playerMeleeActionEvent = Events.PlayerMeleeActionCallback.Start(OnPlayerMeleeAction);
	m_explosionHitEvent = Events.ExplosionHitCallback.Start(OnExplosionHit);
	m_playerDamageEvent = Events.PlayerDamageCallback.Start(OnPlayerDamage);
	m_objectCreatedEvent = Events.ObjectCreatedCallback.Start(OnObjectCreated);
	m_playerWeaponAddedActionEvent = Events.PlayerWeaponAddedActionCallback.Start(OnPlayerWeaponAddedAction);
	m_userMessageCallback = Events.UserMessageCallback.Start(OnUserMessage);
	
	GetMeleeDamageData.Add("NONE",            new DamageData( 1.8f, 1.8f, 0, 3   ,1));
	GetMeleeDamageData.Add("BAT",             new DamageData( 2,    3,    0, 3   ,1)); 
	GetMeleeDamageData.Add("LEAD_PIPE",       new DamageData( 3,    3,    0, 5   ,1)); 
	GetMeleeDamageData.Add("BATON",           new DamageData( 2,    3,    0, 3   ,1)); 
	GetMeleeDamageData.Add("KNIFE",           new DamageData( 3,    1,    0, 8   ,2)); 
	GetMeleeDamageData.Add("AXE",             new DamageData( 4,    2,    0, 8   ,3)); 
	GetMeleeDamageData.Add("KATANA",          new DamageData( 4,    2,    0, 8   ,2)); 
	GetMeleeDamageData.Add("HAMMER",          new DamageData( 1,    5,    0, 5   ,1)); 
	GetMeleeDamageData.Add("CHAIN",           new DamageData( 4,    1,    0, 4   ,1)); 
	GetMeleeDamageData.Add("WHIP",            new DamageData( 4,    1,    0, 4   ,1)); 
	GetMeleeDamageData.Add("PIPE",            new DamageData( 1,    5,    0, 5   ,1)); 
	GetMeleeDamageData.Add("CHAINSAW",        new DamageData( 2,    0,    0, 3   ,1)); 
	GetMeleeDamageData.Add("MACHETE",         new DamageData( 4,    2,    0, 6   ,1)); 
	GetMeleeDamageData.Add("FLAGPOLE",        new DamageData( 2,    3,    0, 4   ,1)); 
	GetMeleeDamageData.Add("CUESTICK_SHAFT",  new DamageData( 3,    1,    0, 3   ,2)); 
	GetMeleeDamageData.Add("CUESTICK",        new DamageData( 2,    2,    0, 6   ,2)); 
	GetMeleeDamageData.Add("CHAIR_LEG",       new DamageData( 1,    2,    0, 3   ,1)); 
	GetMeleeDamageData.Add("CHAIR",           new DamageData( 1,    4,    0, 6   ,1)); 
	GetMeleeDamageData.Add("BROKEN_BOTTLE",   new DamageData( 4,    1,    0, 4   ,1)); 
	GetMeleeDamageData.Add("BOTTLE",          new DamageData( 3,    1,    0, 4   ,1)); 
	GetMeleeDamageData.Add("BASEBOLL",        new DamageData( 2,    2,    0, 2   ,1)); 
	GetMeleeDamageData.Add("PILLOW",          new DamageData( 0,    2,    0, 0   ,1)); 
	GetMeleeDamageData.Add("TRASHCAN_LID",    new DamageData( 2.5f, 2.5f, 0, 4   ,1)); 
	GetMeleeDamageData.Add("TRASH_BAG",       new DamageData( 1,    2,    0, 5   ,1)); 
	GetMeleeDamageData.Add("TEAPOT",          new DamageData( 3,    2.5f, 0, 5   ,1));
	GetMeleeDamageData.Add("SHURIKEN",        new DamageData( 5,    1,    0, 8   ,1));
	
	GetProjectileDamageData.Add("PISTOL",          new DamageData( 1,    3,    0, 10   , 1));
	GetProjectileDamageData.Add("PISTOL45",        new DamageData( 2,    4,    0, 10   , 2));
	GetProjectileDamageData.Add("SILENCEDPISTOL",  new DamageData( 2,    2,    0, 10   , 1));
	GetProjectileDamageData.Add("MACHINE_PISTOL",  new DamageData( 1,    1.5f, 0, 5    , 1));
	GetProjectileDamageData.Add("MAGNUM",          new DamageData( 3,    6,    0, 17   , 2));
	GetProjectileDamageData.Add("REVOLVER",        new DamageData( 3,    6.5f, 0, 17   , 2));
	GetProjectileDamageData.Add("FLAREGUN",        new DamageData( 0,    5,    5, 4    , 1));
	GetProjectileDamageData.Add("SMG",             new DamageData( 2,    2,    0, 5    , 1));
	GetProjectileDamageData.Add("UZI",             new DamageData( 2,    2,    0, 5    , 1));
	GetProjectileDamageData.Add("SILENCEDUZI",     new DamageData( 2.5f, 2,    0, 5    , 1));
	GetProjectileDamageData.Add("TOMMYGUN",        new DamageData( 3.5f, 2.5f, 0, 10   , 1));
	GetProjectileDamageData.Add("MP50",            new DamageData( 3.5f, 2.8f, 0, 10   , 1));
	GetProjectileDamageData.Add("ASSAULT",         new DamageData( 3.5f, 3.5f, 0, 10   , 2));
	GetProjectileDamageData.Add("SHOTGUN",         new DamageData( 3,    3,    0, 8    , 1));
	GetProjectileDamageData.Add("DARK_SHOTGUN",    new DamageData( 3,    3,    0, 8    , 1));
	GetProjectileDamageData.Add("SAWED_OFF",       new DamageData( 3,    3,    0, 8    , 1));
	GetProjectileDamageData.Add("SUB_MACHINEGUN",  new DamageData( 3.5f, 3,    0, 7    , 1));
	GetProjectileDamageData.Add("M60",             new DamageData( 1,    2,    0, 10   , 1));
	GetProjectileDamageData.Add("CARBINE",         new DamageData( 3,    6,    0, 15   , 2));
	GetProjectileDamageData.Add("BOW",             new DamageData( 3,    6,    0, 20   , 2));
	GetProjectileDamageData.Add("SNIPER",          new DamageData( 4,    6,    0, 15   , 2));
	GetProjectileDamageData.Add("GRENADE_LAUNCHER",new DamageData( 0,    0,    0, 0    , 0));
	GetProjectileDamageData.Add("FLAKCANNON",      new DamageData( 0,    0,    0, 50   , 0));

}

Events.ProjectileHitCallback m_projectileHitEvent = null;
Events.PlayerMeleeActionCallback m_playerMeleeActionEvent = null;
Events.ExplosionHitCallback m_explosionHitEvent = null;
Events.PlayerDamageCallback m_playerDamageEvent = null;
Events.ObjectCreatedCallback m_objectCreatedEvent = null;
Events.PlayerWeaponAddedActionCallback m_playerWeaponAddedActionEvent = null;
Events.UserMessageCallback m_userMessageCallback = null;

public void OnPlayerWeaponAddedAction(IPlayer Ply, PlayerWeaponAddedArg Args){
	if(Ply == null || Ply.IsRemoved)return;
	BodyProperties BodyProp = GetBodyPropertiesByIPlayer(Ply);
    if(Args.WeaponItem == WeaponItem.PILLS){
		Armour Arm = Armour.GetArmour(-1);
		Arm.ApplyArmour(BodyProp);
	}
	else if(Args.WeaponItem == WeaponItem.MEDKIT){
		foreach(Organ Org in BodyProp.AllOrgans.Values){
			Org.Health += Org.InitialHealth / 2;
		}
		BodyProp.CheckMaxBlood();
	}
}
public void OnProjectileHit(IProjectile Proj, ProjectileHitArgs Args){
	if(!GetProjectileDamageData.ContainsKey(Proj.ProjectileItem.ToString()) || Args.Damage <= 0)return;
	if(Args.IsPlayer){
		IPlayer Ply = (IPlayer)Game.GetObject(Args.HitObjectID);
		BodyProperties BodyProp = GetBodyPropertiesByIPlayer(Ply);if(BodyProp.GetBodyPartByLocal(null,true) == null || Ply == null)return;
		bool WasDead = Ply.IsDead;

		DamageData DmgData = GetProjectileDamageData[Proj.ProjectileItem.ToString()].Copy();
		BodyProperties.BodyPart BdyPart = null;
		
		BdyPart = BodyProp.GetBodyPartByLocal(Proj.Position,true);
		if(BdyPart == null)BdyPart = BodyProp.GetBodyPartByLocal(Proj.Position,false);
		if(Ply.IsDead || BdyPart == null)
			BdyPart = BodyProp.GetBodyPartByLocal(null,false);
		
		Vector2 ImpactLinearVelocity = Proj.Velocity / 300;
		DmgData.HitArguments = Proj;
		DmgData.HitPosition = Args.HitPosition;
		DmgData.HitImpulse = ImpactLinearVelocity;
		
		if(!WasDead && Ply.IsDead)Ply.SetLinearVelocity(ImpactLinearVelocity + new Vector2(0,DmgData.BluntDamage/2));
		
		BdyPart.DefaultDamage(DmgData.Copy());
	}
}

public void OnPlayerMeleeAction(IPlayer DamagerPly, PlayerMeleeHitArg[] ArgsA) {
	foreach(PlayerMeleeHitArg Args in ArgsA.Where(Args => Args.HitDamage > 0)){
		if(Args.IsPlayer){
			IPlayer DamagedPly = (IPlayer)Game.GetObject(Args.ObjectID);
			BodyProperties DamagedBodyProp = GetBodyPropertiesByIPlayer(DamagedPly);if(DamagedPly == null)return;
			BodyProperties DamagerBodyProp = GetBodyPropertiesByIPlayer(DamagerPly);
			bool WasDead = DamagedPly.IsDead;
			
			DamageData DmgData;
			try{
			if(DamagerPly.CurrentMeleeMakeshiftWeapon.WeaponItem != WeaponItem.NONE)
				DmgData = GetMeleeDamageData[DamagerPly.CurrentMeleeMakeshiftWeapon.WeaponItem.ToString()].Copy();
			else 
				DmgData = GetMeleeDamageData[DamagerPly.CurrentMeleeWeapon.WeaponItem.ToString()].Copy();
			}catch(Exception){DmgData = GetMeleeDamageData["NONE"].Copy();}
			
			float Multiplier = DamagerBodyProp.MeleeForceMultiplier() * DamagedPly.GetModifiers().MeleeDamageTakenModifier;
			DmgData.SharpDamage *= Multiplier;
			DmgData.BluntDamage *= Multiplier;

			BodyProperties.BodyPart BdyPart = null;
			BdyPart = DamagedBodyProp.GetBodyPartByLocal(DamagerBodyProp.PunchArea(),true);
			if(BdyPart == null)BdyPart = DamagedBodyProp.GetBodyPartByLocal(DamagerBodyProp.PunchArea(),false);
			if(DamagedPly.IsDead || BdyPart == null)
				BdyPart = DamagedBodyProp.GetBodyPartByLocal(null,false);
			
			
			Vector2 ImpactLinearVelocity = (DamagedPly.GetLinearVelocity() + DamagerPly.GetLinearVelocity()) + new Vector2(0,DmgData.BluntDamage);
			DmgData.HitArguments = Args;
			DmgData.HitImpulse = ImpactLinearVelocity;
			DmgData.HitPosition = BdyPart.GetEffectPosition();

			if(!WasDead && DamagedPly.IsDead)if(Math.Abs(ImpactLinearVelocity.X) + Math.Abs(ImpactLinearVelocity.Y) > 4)DamagedPly.SetLinearVelocity(ImpactLinearVelocity);
			
			BdyPart.DefaultDamage(DmgData.Copy());
		}
	}
}

public void OnPlayerDamage(IPlayer Ply, PlayerDamageArgs Args){
	if(Ply == null)return;
	if(Ply.IsRemoved || Args.DamageType == PlayerDamageEventType.Other)return;

	if(!Ply.IsDead)Ply.SetHealth(Ply.GetHealth() + Args.Damage);
	
	BodyProperties BodyProp = GetBodyPropertiesByIPlayer(Ply);
	BodyProp.CheckMaxBlood();
		
	if(Args.DamageType == PlayerDamageEventType.Fall){
		
		List<BodyProperties.BodyPart> NFBdyParts = new List<BodyProperties.BodyPart>{
			BodyProp.AllBodyParts["Legs"],
			BodyProp.AllBodyParts["Arms"],
			BodyProp.AllBodyParts["Head"]
		};
		if(BodyProp.WasFalling){
			NFBdyParts.Add(BodyProp.AllBodyParts["Head"]);
			NFBdyParts.Add(BodyProp.AllBodyParts["Head"]);
		}
		
		List<BodyProperties.BodyPart> BdyParts = NFBdyParts.Where(BdyPart => GetAllHealth(BdyPart.ExposedOrgans) > 0).ToList();
		if(BdyParts.Count <= 0){BdyParts = BodyProp.AllBodyParts.Values.Where(BdyPart => GetAllHealth(BdyPart.ExposedOrgans) > 0).ToList();}
		if(BdyParts.Count <= 0){BdyParts = BodyProp.AllBodyParts.Values.ToList();}
		DamageData DmgData = new DamageData(Args.Damage/5,Args.Damage/5,Math.Abs(Ply.GetLinearVelocity().X)/2.5f,Args.Damage,1);
		DmgData.SharpDamage *= GlobalMultipliers["FDM"];
		DmgData.BluntDamage *= GlobalMultipliers["FDM"];
		DmgData.ThermalDamage *= GlobalMultipliers["FDM"];
		
		DmgData.HitArguments = Args;
		DmgData.HitImpulse = new Vector2(0,Math.Min(DmgData.BluntDamage,5));
		
		for(int i = 0;i < rnd.Next(1,3);i++){
			BodyProperties.BodyPart BdyPart = BdyParts[rnd.Next(0,BdyParts.Count)];
			DmgData.HitPosition = BdyPart.GetEffectPosition();
			BdyPart.DefaultDamage(DmgData.Copy());
		}
	}
	else if(Args.DamageType == PlayerDamageEventType.Missile){
		try{
		
		IObject WeaponObject = Game.GetObject(Args.SourceID);
		DamageData DmgData = new DamageData(1,1,1,1,1);
		DmgData.HitPosition = WeaponObject.GetWorldPosition();
		
		Vector2 ObjVelocity = WeaponObject.GetLinearVelocity();
		Vector2 PlyVelocity = Ply.GetLinearVelocity();
		BodyProperties.BodyPart BdyPart;
		try{BdyPart = BodyProp.GetBodyPartByLocal(new Vector2(Ply.GetWorldPosition().X,WeaponObject.GetWorldPosition().Y),true);}catch(Exception){return;}
		
		DmgData.BluntDamage+=WeaponObject.GetMass() * 350;
		if(WeaponObject is IObjectWeaponItem){
			IObjectWeaponItem WeaponItem = (IObjectWeaponItem)WeaponObject;
			if(WeaponItem.WeaponItemType == WeaponItemType.Melee){
				DmgData = GetMeleeDamageData[WeaponItem.WeaponItem.ToString()].Copy();
				DmgData.BluntDamage+=WeaponObject.GetMass() * 350;
			}
		}
		else{
			DmgData.SharpDamage+=DmgData.BluntDamage/2;
			DmgData.PenetrationFactor=1;
		}
		
		DmgData.BluntDamage*=Math.Abs((ObjVelocity.X - PlyVelocity.X) + (ObjVelocity.Y - PlyVelocity.Y)) / 15;
		
		Vector2 ImpactLinearVelocity = WeaponObject.GetLinearVelocity() / 2;
		DmgData.HitArguments = Args;
		DmgData.HitImpulse = ImpactLinearVelocity;
		DmgData.HitPosition = BdyPart.GetEffectPosition();
		
		BdyPart.DefaultDamage(DmgData);
		
		}catch(Exception){return;} //Here randomly unknown error occurs. I don't know the reason, and I'm too lazy to fix it.
	}
	else if(Args.DamageType == PlayerDamageEventType.Fire){
		BodyProperties.BodyPart BdyPart;
		try{BdyPart = BodyProp.GetBodyPartByLocal(null,true);}catch(Exception){return;}
		DamageData DmgData = new DamageData(0,0,Args.Damage/1.5f,0,1);
		
		DmgData.HitArguments = Args;
		DmgData.HitImpulse = new Vector2(0,4);
		DmgData.HitPosition = BdyPart.GetEffectPosition();
		
		BdyPart.DefaultDamage(DmgData.Copy());
	}
}

public void OnExplosionHit(ExplosionData Explosion, ExplosionHitArg[] ArgsA){
	foreach(ExplosionHitArg Args in ArgsA){
		if(Args.IsPlayer && Args.HitType == ExplosionHitType.Damage){
			IPlayer Ply = (IPlayer)Args.HitObject;
			BodyProperties BodyProp = GetBodyPropertiesByIPlayer(Ply);if(BodyProp == null)return;
			
			DamageData DmgData = new DamageData(Args.Damage/20,Args.Damage/20,Args.Damage/5,Args.Damage/3,1);
			for(int i = 0;DmgData.GetTotalDamage() > i;i++){
				BodyProperties.BodyPart BdyPart = BodyProp.GetBodyPartByLocal(null,true);if(BdyPart==null)return;
				
				Vector2 ImpactLinearVelocity = (Ply.GetLinearVelocity() + new Vector2(rnd.Next(-3,3),10))/2;
				
				DmgData.HitArguments = Args;
				DmgData.HitImpulse = ImpactLinearVelocity;
				DmgData.HitPosition = BdyPart.GetEffectPosition();
				BdyPart.DefaultDamage(DmgData.Copy());
			}
		}
	}
}

public static bool DynamicEffects = true;
public static Dictionary<string,float> GlobalMultipliers = new Dictionary<string,float>();
public void AddMultipliers(){
	GlobalMultipliers.Add("HM",1);
	GlobalMultipliers.Add("AHM",1);
	GlobalMultipliers.Add("LSM",0.6f);
	GlobalMultipliers.Add("BLEM",1);
	GlobalMultipliers.Add("PDM",1);
	GlobalMultipliers.Add("MDM",1);
	GlobalMultipliers.Add("FDM",1);
	GlobalMultipliers.Add("CSM",1);
	
	foreach(string key in GlobalMultipliers.Keys.ToList()){
		if(Game.LocalStorage.ContainsKey(key+"_HD"))
		GlobalMultipliers[key] = (float)Game.LocalStorage.GetItem(key+"_HD");
	}
}

public void OnUserMessage(UserMessageCallbackArgs Args){
	IUser User = Args.User;
	if((!User.IsModerator && !User.IsHost) || !Args.IsCommand)return;
	string Command = Args.Command.ToUpper();
	string CommandArg = Args.CommandArguments.ToUpper();
	if(Command == "CLEAR_MULTIPLIERS"){
		Game.LocalStorage.Clear();
		Game.ShowChatMessage("All LocalStorage has been removed.", Color.Green, User.UserIdentifier);
	}
	else if(Command == "DYNAMIC_EFFECTS"){
		DynamicEffects = !DynamicEffects;
		Game.LocalStorage.SetItem("DYNAMIC_EFFECTS_HD", DynamicEffects);
	}
	else if(Command == "HELP_HD"){
		Game.ShowChatMessage("-----Multipliers-----", Color.Red, User.UserIdentifier);
		Game.ShowChatMessage("To change the multiplier just type '/[Multiplier name] [float number]'", Color.Blue, User.UserIdentifier);
		Game.ShowChatMessage("HM [Health Multiplier]", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("AHM [Armor Health Multiplier]", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("LSM [Leg Stat Multiplier] (Change the player speed when hit in the leg.)", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("BLEM [Bleeding Multiplier] (Changes the amount of blood lost in a slashing damage.)", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("FDM [Fall Damage Multiplier]", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("CSM [Camera Shake Multiplier]", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("-----Commands-----", Color.Red, User.UserIdentifier);
		Game.ShowChatMessage("'/Clear_Multipliers' to reset all multipliers.", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("'/Dynamic_Effects' Remove all effects.", Color.Yellow, User.UserIdentifier);
		Game.ShowChatMessage("-----Notes-----", Color.Red, User.UserIdentifier);
		Game.ShowChatMessage("When a player get 'Pills' (+25) he instantly get a random armour.", Color.Yellow, User.UserIdentifier);
	}
	else {
		if(GlobalMultipliers.Keys.ToList().Contains(Command)){
			if(CommandArg == ""){
				Game.ShowChatMessage("The "+Command+" is "+GlobalMultipliers[Command].ToString()+".", Color.Blue, User.UserIdentifier);
			}else{
				try{
					try{
						Game.LocalStorage.SetItem(Command+"_HD", float.Parse(CommandArg));
					}
					catch(FormatException){
						Game.LocalStorage.SetItem(Command+"_HD", float.Parse(Double.Parse(CommandArg).ToString()));
					}
					GlobalMultipliers[Command] = float.Parse(CommandArg);
					
					Game.ShowChatMessage("'"+Command+"' now is '"+CommandArg+"'", Color.Green, User.UserIdentifier);
				}catch(FormatException){
					Game.ShowChatMessage("FormatException (C#): The argument used does not match the 'float' or 'int' format.", Color.Yellow, User.UserIdentifier);
				}
			}
		}
	}
}

//hi :)