class WhooshDamage extends SqRootScript
{
	function OnDamage()
	{
		local target = message().culprit;
		
		if(Property.Get(target,"PhysControl","Controls Active") == 0)
		{
			local dist = Object.Position(target) - Object.Position(self);
			local dir = dist.GetNormalized();
			local vel = vector(0);
			Physics.GetVelocity(target,vel);
			Physics.SetVelocity(target,vel + dir*M.max(0,(40 - dist.Length()*3)));
		}
	}
}

class WhooshAttack extends SqRootScript
{
	function OnStartAttack()
	{
		if(!Link.AnyExist("AITarget",self))
			return;
		local target = LinkDest(Link.GetOne("AITarget",self));
		
		if(Property.Get(target,"PhysControl","Controls Active") == 0)
		{
			local dist = Object.Position(target) - Object.Position(self);
			local dir = dist.GetNormalized();
			local vel = vector(0);
			Physics.GetVelocity(target,vel);
			Physics.SetVelocity(target,vel + dir*M.max(0,(40 - dist.Length()*3)));
		}
	}
}

class HackedHitbox extends SqRootScript
{
	function UnPhys()
	{
		Property.Set(self, "StTweqBlink", "AnimS", 0);
		
		local copy = GetData("HHB");
		
		Property.SetSimple(copy, "HasRefs", false);
		Property.SetSimple(copy, "CollisionType", 0);
		
		Property.Set(self, "StTweqBlink", "Cur Time", 0);
		Property.Set(self, "StTweqBlink", "Frame #", 0);
		
		Object.Teleport(copy,vector(0),vector(0));
	}
	
	function Phys()
	{
		Property.Set(self, "StTweqBlink", "Cur Time", 0);
		Property.Set(self, "StTweqBlink", "Frame #", 0);
		Property.Set(self, "StTweqBlink", "AnimS", 1);
		
		local copy = GetData("HHB");
		
		Property.SetSimple(copy, "HasRefs", true);
		Property.SetSimple(copy, "CollisionType", 1);
		Object.Teleport(copy,vector(0),vector(0),self);
	}
	
	function OnTweqComplete()
	{
		Property.Set(self, "StTweqBlink", "Cur Time", 0);
		Property.Set(self, "StTweqBlink", "Frame #", 0);
		local copy = GetData("HHB");
		Object.Teleport(copy,vector(0),vector(0),self);
		Property.Set(self, "StTweqBlink", "AnimS", 1);
	}
	
	function OnDamage()
	{
		if("DmgHitbox" in userparams())
		{
			local host = GetData("Host");
			if(host == 0)
				return;
			local alertness = Property.Get(host,"AI_Alertness","Level");
			Damage.Damage(host, message().culprit, message().damage * (0.25 + 0.25 * (3-alertness)), message().kind);
		}
	}

	function OnFollow()
	{
		Phys();
	}
	
	function OnStop()
	{
		UnPhys();
	}
	
	function OnSetHost()
	{
		SetData("Host",message().data);
	}
	
	function OnSet()
	{
		local copy = Object.Create(self);
		SetData("HHB",copy);
		SetData("Host",message().data);
		PostMessage(copy,"SetHost",self);
		
		Link.Create("~CulpableFor",copy,message().data);
		
		Property.Set(copy, "PhysControl", "Controls Active", 24);
		Property.Set(self, "PhysType", "Type", 3);
		
		Property.Set(self, "CfgTweqBlink", "Halt", 2);
		Property.Set(self, "CfgTweqBlink", "AnimC", 2);
		Property.Set(self, "CfgTweqBlink", "Rate", 120);
		UnPhys();
	}
}

class ExtraHitbox extends SqRootScript
{
	limb = {
		"N/A"				:0, 
		"Head"				:1, 
		"Neck"				:2, 
		"Abdomen"			:3, 
		"Butt"				:4, 
		"Left Shoulder"		:5, 
		"Right Shoulder"	:6, 
		"Left Elbow"		:7, 
		"Right Elbow"		:8, 
		"Left Wrist"		:9, 
		"Right Wrist"		:10, 
		"Left Fingers"		:11, 
		"Right Fingers"		:12, 
		"Left Hip"			:13, 
		"Right Hip"			:14, 
		"Left Knee"			:15, 
		"Right Knee"		:16, 
		"Left Ankle"		:17, 
		"Right Ankle"		:18, 
		"Left Toe"			:19, 
		"Right Toe"			:20, 
		"Tail"				:21
	}
	
	function ActivateHitbox()
	{
		//print(Object.GetName(Object.Archetype(GetData("HHitbox"))));
		PostMessage(GetData("HHitbox"),"Follow");
	}
	
	function DisableHitbox()
	{
		PostMessage(GetData("HHitbox"),"Stop");
	}
	
	function OnAIModeChange()
	{
		if("CombatOnly" in userparams() && message().mode == 4)
			ActivateHitbox();
		else if ("CombatOnly" in userparams())
			DisableHitbox();
		else if (message().mode == 5)
			DisableHitbox();
		else
			return;
	}

	function OnSim()
	{
		if(!IsDataSet("HHitbox"))
		{	
			local def_hb = ObjID("Marker");

			if("HackedHitbox" in userparams())
				if(ObjID(userparams().HackedHitbox) != 0)
					def_hb = ObjID(userparams().HackedHitbox);
			
			local hb = Object.Create(def_hb);
			Object.Teleport(hb,vector(0),vector(0),self);
			SetData("HHitbox",hb);
			SendMessage(hb,"Set",self);
			
			local def_limb = 0;
			if("Limb" in userparams())
			{
				if(userparams().Limb in limb)
					def_limb = limb[userparams().Limb];
				else
					def_limb = M.tryInteger(userparams().Limb);
			}
			local def_pos = vector(0);
			if("PosOffSet" in userparams())
				def_pos = M.StringToVec(userparams().PosOffSet);
			local rot_pos = vector(0);
			if("RotOffSet" in userparams())
				rot_pos = M.StringToRVec(userparams().RotOffSet);
			
			
			local link = Link.Create("DetailAttachement",hb,self);
			LinkTools.LinkSetData(link, "Type", 2);
			LinkTools.LinkSetData(link, "joint", def_limb);
			LinkTools.LinkSetData(link, "rel pos", def_pos);
			LinkTools.LinkSetData(link, "rel rot", rot_pos);
			
			if("CombatOnly" in userparams())
				return;
			ActivateHitbox();
		}
	}
}

class CrumbleOnDeath extends SqRootScript
{
	function SetDelete(obj)
	{
		Property.Set(obj, "CfgTweqDelete", "Halt", 4);
		Property.Set(obj, "CfgTweqDelete", "AnimC", 1);
		Property.Set(obj, "CfgTweqDelete", "Rate", 5000);
		Property.Set(obj, "StTweqDelete", "AnimS", 1);
		
		Property.Set(obj, "StTweqJoints", "AnimS", 0);
		Property.Set(obj, "StTweqModels", "AnimS", 0);
		Property.Set(obj, "StTweqRotate", "AnimS", 0);
		Property.Set(obj, "StTweqScale", "AnimS", 0);
		
		Property.Set(obj, "PhysType", "Type", 2);
		Property.Set(obj, "PhysType", "# Submodels", 1);
		Property.Set(obj, "PhysControl", "Controls Active", 0);
		
		Property.Remove(obj, "FrobInfo");
	}
	
	function OnSlain()
	{
		foreach(l in Link.GetAll("~DetailAttachement",self))
		{
			local o = LinkDest(l);
			local n = Object.Create(o);
			local rnd_vel = vector(Data.RandFltNeg1to1(),Data.RandFltNeg1to1(),Data.RandFltNeg1to1());
			rnd_vel.x *= Data.RandInt(5,10);
			rnd_vel.y *= Data.RandInt(5,10);
			rnd_vel.z *= Data.RandInt(5,10);
			SetDelete(n);
			Object.Teleport(n, vector(0), vector(0), o);
			Physics.SetVelocity(n,rnd_vel);		
		}
		if("DeathExtra" in userparams())
		{
			local n = Object.Create(ObjID(userparams().DeathExtra));
			local rnd_vel = vector(Data.RandFltNeg1to1(),Data.RandFltNeg1to1(),Data.RandFltNeg1to1());
			rnd_vel.x *= Data.RandInt(5,10);
			rnd_vel.y *= Data.RandInt(5,10);
			rnd_vel.z *= Data.RandInt(5,10);
			Object.Teleport(n, vector(0), vector(0), self);
			Physics.SetVelocity(n,rnd_vel);	
		}
		Damage.Slay(self,message().culprit);
	}
}

class Gerris extends SqRootScript
{
	function pos()
	{
		return Object.Position(self);
	}
	
	function stopJoints()
	{
		SetProperty("StTweqJoints","AnimS",0);
		for(local id = 1;id<7;id++)
		{	
			Property.Set(self,"CfgTweqJoints","Joint"+ id +"AnimC" ,0);
			Property.Set(self,"JointPos","Joint " + id,0);
		}
	}
	
	function resetJoints()
	{
		for(local id = 1;id<7;id++)
		{	
			local l = Property.Get(self,"CfgTweqJoints","    rate-low-high").y;
			if(id > 1)
				l = Property.Get(self,"CfgTweqJoints","    rate-low-high" + id).y;
			Property.Set(self,"JointPos","Joint " + id,l);
		}
	}
	
	function goal()
	{
		if(IsDataSet("Goal")) return M.StringToVec(GetData("Goal"));
		return pos();
	}
	
	function objPos(obj)
	{
		return Object.Position(obj);
	}
	
	function setGoal(vec)
	{
		Physics.SubscribeMsg(self,ePhysScriptMsgType.kCollisionMsg);
		SetData("Goal",M.VecToString(vec));
	}

	function SelectRandomDest()
	{
		return (pos() + vector(M.sign(Data.RandFltNeg1to1()) * 10, M.sign(Data.RandFltNeg1to1()) * 10, 0));
	}
	
	function OnPhysCollision()
	{
		setGoal(SelectRandomDest());
	}
	
	function GoTowardGoal()
	{
		if(IsDataSet("Goal"))
		{	
			Property.Set(self,"CfgTweqJoints","AnimC" ,1);
			
			local diff = goal() - pos();
			local dir = diff.GetNormalized();
			local max_speed = GetProperty("AI_MoveSpeed");
			local vel = dir*max_speed;
			vel.z = 1;
			Physics.SetVelocity(self,vel);
			
			local max_hex = 65535;
			local hex_coef = 65535 / 360;
			local face = floor((M.RadToDeg(atan2(vel.y,vel.x))+180) * hex_coef);
			if (face > max_hex) face -= max_hex;
			else if (face < 0) face += max_hex;
			
			Property.Set(self,"Position","Heading",face);
			Property.Set(self,"Position","Pitch",0);
			Property.Set(self,"Position","Bank",0);
			SetProperty("StTweqJoints","AnimS",1);
			
			if(diff.Length() < 4.5)
			{
				Physics.UnsubscribeMsg(self,ePhysScriptMsgType.kCollisionMsg);
				//print("GOAL REACHED ----------------------------------");
				if(Link.AnyExist("AICurrentPatrol",self))
				{
					local marker = LinkDest(Link.GetOne("AICurrentPatrol",self));
					if (Object.Position(marker) == goal())
					{
						Link.Destroy(Link.GetOne("AICurrentPatrol",self,marker));
						//print("SUCCESS");
						if(Link.AnyExist("AIPatrol",marker))
						{
							Link.Create("AICurrentPatrol",self,LinkDest(Link.GetOne("AIPatrol",marker)));
						}
					}
				}
				
				ClearData("Goal");
			}
			resetJoints();
		}
	}


	function ResumeBehaviour()
	{
		SetProperty("StTweqBlink","Frame #",0);
		SetProperty("StTweqBlink","AnimS",1);
		
		local dest = SelectRandomDest();
		if(Link.AnyExist("AICurrentPatrol",self))
			dest = objPos( LinkDest(Link.GetOne("AICurrentPatrol",self)) );
		
		//print(dest);
		
		if(GetProperty("AI_Patrol"))
		{
			setGoal(dest);
		}else stopJoints();
	}
	
	function OnBeginScript()
	{
		SetProperty("PhysAttr","Density",0.1);
		Property.Add(self,"SelfLit");
	}

	function OnSlain()
	{
		SetProperty("AI","Behavior set","");
		SetProperty("StTweqBlink","AnimS",0);
		stopJoints();
		SetProperty("PhysControl","Controls Active",0);
		SetProperty("PhysAttr","Rotation Axes",1);
		SetProperty("PhysAttr","Rest Axes",32);
		//SetProperty("FrobInfo","World Action",1);
		SetProperty("FrobInfo","Inv Action",1);
		SetProperty("CollisionType",0);
		Sound.HaltSpeech(self);
	}


	function OnTweqComplete()
	{
		if(message().Type == eTweqType.kTweqTypeFlicker)
		{
			if(GetProperty("HitPoints")<= 0)
			{
				OnSlain();
				return;
			}
			if(!IsDataSet("Goal"))
				ResumeBehaviour();
			else
				GoTowardGoal();
		}
	}
}

//Hack to fix the hitboxes which are supposed to be hard coded
class Crit extends SqRootScript
{
	function FixMe()
	{
		//SetProperty("PhysControl","Controls Active",8);
		/*if(Object.Exists(ObjID("BashProof")))
		{
			Object.AddMetaProperty(self,"BashProof");
			SetOneShotTimer("RestoreBash",0.5);
		}*/
		Physics.SetVelocity(self,vector(0));

		SetProperty("PhysControl","Controls Active", 24);
		
	}

	function OnBeginScript()
	{
		if(GetProperty("AI_Mode") != 5)
			FixMe();
	}
	
	function OnAIModeChange()
	{
		if(GetProperty("AI_Mode") != 5)
			FixMe();
	}
	
	function OnTimer()
	{
		if(message().name == "RestoreBash")
			Object.RemoveMetaProperty(self,"BashProof");
	}
}

class Horse extends Crit
{
	function FixMe()
	{	
		base.FixMe();
		SetProperty("PhysDims","Radius 1",1.95);
		SetProperty("PhysDims","Radius 2",1.75);
		SetProperty("PhysDims","Offset 2",vector(5,0,0));
	}
}

class Sweel extends Crit
{
	function FixMe()
	{
		base.FixMe();
		SetProperty("PhysDims","Radius 1",0.15);
		SetProperty("PhysDims","Radius 2",0.35);
	}
}


class Dog extends Crit
{
	function FixMe()
	{
		base.FixMe();
		SetProperty("PhysType","# Submodels",1);
		SetProperty("PhysDims","Radius 1",0.55);
	}
}

class CustomCritSize extends Crit
{
	function FixMe()
	{
		base.FixMe();
		local submodel_num = 2;
		local size1 = 0.25;
		local size2 = 0.25;
	
		if("Submodels" in userparams())
		{
			submodel_num = userparams().Submodels;
			SetProperty("PhysType","# Submodels",submodel_num);
		}
		if("Radius1" in userparams())
			size1 = userparams().Radius1;
		if("Radius2" in userparams())
		{
			size2 = userparams().Radius2;
			SetProperty("PhysDims","Radius 2",size2);
			if("Offset2" in userparams())
				SetProperty("PhysDims","Offset 2",M.StringToVec(userparams().Offset2));
		}
		SetProperty("PhysDims","Radius 1",size1);	
	}
}

//For zombies with PossumAtStart
//Fix countless issues and less work in the gamesyst
class PossumAtStart extends SqRootScript
{
	function OnSim()
	{	
		Sound.HaltSpeech(self);
		
		if(Object.HasMetaProperty(self,"M-FakeSlainZombie"))
			Object.RemoveMetaProperty(self,"M-FakeSlainZombie");
		
		if(!Object.HasMetaProperty(self,"M-ZombiePossum"))
			Object.AddMetaProperty(self,"M-ZombiePossum");
		
		if(!Link.AnyExist("AIWatchObj",self,self))
			Link.Create("AIWatchObj",self,self);
	}
	
	function OnLivingStimStimulus()
	{
		//Special Stimulus for livings who can wake
		//the zombie up. To use when needed, 
		//we don't need useless stim loop for those
		//who will never meet a zombie.
		SendMessage(self,"Resurrect");
	}
	/*
	function OnTouchStimStimulus()
	{
		SendMessage(self,"Resurrect");
	}
	*/
}

//Improved ZombieRegen script so it could be more reliable
//and customizable.
class TBPZombie extends SqRootScript
{
	def_parts={
					head 	=	"ZomHead"
					leg		=	"ZomLeg"
					hand	=	"ZomHand"
					arm		=	"ZomArm"
					upperarm=	"ZomUpperArm"
					arm_hand=	"ZomArm+Hand"
					pelv_thi=	"ZomPelvis+Thigh"
					leg_foot=	"ZomLeg+Foot"
					torso	=	"ZomTorso"
					foot	=	"ZomFoot"
				};
	
	
	function MakeVecFromStr(str)
	{
		if(typeof str == "string")
		{
			local arr = split(str,",");
			if(arr.len() == 3)
				return vector((arr[0].tofloat()),(arr[1].tofloat()),(arr[2].tofloat()));
			else
				return vector(0);
		}
		return vector(0);
	}
	
	function SetTextures(obj, part)
	{
		for(local i = 0 ; i < 4 ; i++)
		{
			if((part + "_txt" + i.tostring()) in userparams())
				Property.SetSimple(obj,("OTxtRepr" + i.tostring()),userparams()[(part + "_txt" + i.tostring())]);
			else if(("global_txt" + i.tostring()) in userparams())
				Property.SetSimple(obj,("OTxtRepr" + i.tostring()),userparams()[("global_txt" + i.tostring())]);
		}
	}
	
	function SetModel(obj, part)
	{
		//If you want a specific model, it means it's more important than the prefix
		//So let's give the priority and ignore the prefix
		if((part + "_model") in userparams())
			Property.SetSimple(obj,"ModelName",userparams()[(part + "_model")]);
		else if("modelPrefix" in userparams())
		{
			if(part.slice(0,part.len()-1) != "extra")
					Property.SetSimple(obj,"ModelName",(userparams().modelPrefix + Property.Get(obj,"ModelName")));
		}
	}
	
	function SetMaterial(obj, part)
	{
		//Apply a material to your precious limb
		if((part + "_mat") in userparams())
			Property.Set(obj,"Material Tags","1: Tags","Material "+(userparams()[(part + "_mat")]));
	}
	
	function SetStr(obj, part)
	{
		//Apply a material to your precious limb
		if((part + "_str") in userparams())
			Property.SetSimple(obj,"GameName",(userparams()[(part + "_str")]));
	}
	
	function SetScale(obj, part)
	{
		//Apply a material to your precious limb
		if((part + "_scale") in userparams())
			Property.SetSimple(obj,"Scale",MakeVecFromStr(userparams()[(part + "_scale")]));
	}
	
	function SetMass(obj, part)
	{
		//Apply a mass to your precious limb
		if((part + "_mass") in userparams())
			Property.Set(obj,"PhysAttr","Mass",(userparams()[(part + "_mass")]));
	}
	
	function SetGravity(obj, part)
	{
		//Apply a gravity to your precious limb
		if((part + "_gravity") in userparams())
			Property.Set(obj,"PhysAttr","Gravity %",(userparams()[(part + "_gravity")]));
	}
	
	function SetRestAxes(obj, part)
	{
		//Apply the rest axes to your precious limb
		if((part + "_restAxes") in userparams())
		{
			local axes = (userparams()[(part + "_restAxes")]);
			try
			{
				Property.Set(obj,"PhysAttr","Rest Axes",axes.tointeger());
			}
			catch(err)
			{
				local flags = 0;
				local arr = split((axes).tostring(),",");
				foreach(x in arr)
				{
					if(x.tolower() == "+x")
						flags = flags | 1;
					if(x.tolower() == "-x")
						flags = flags | 8;
					if(x.tolower() == "+y")
						flags = flags | 2;
					if(x.tolower() == "-y")
						flags = flags | 16;
					if(x.tolower() == "+z")
						flags = flags | 4;
					if(x.tolower() == "-z")
						flags = flags | 32;
				}
				
				Property.Set(obj,"PhysAttr","Rest Axes",flags);
			}
		}
	}

	function FixParts(parts)
	{
		local res = {};
		foreach(k,v in parts)
		{
			if(k in userparams())
				res[k] <- (userparams()[k]).tostring();
			else
				res[k] <- v;
		}
		return res;
	}

	function SetPrefix(parts)
	{
		local res = {};
		foreach(k,v in parts)
		{
			if(v!=0)
				res[k] <- (userparams().prefix).tostring() + v;
			else
				res[k] <- 0;
		}
		
		return res;	
	}

	function GetObjectId(obj_or_id)
	{
		if(typeof obj_or_id == "string")
		{
			local res = 0;
			try 
			{
				res = obj_or_id.tointeger();
			}
			catch(err)
			{
				res = ObjID(obj_or_id);
			}
			return res;
		}
		return 0;
	}
	
	function Unfreeze()
	{
		if(IsDataSet("MotSet"))
		{
			SetProperty("MotActorTagList","tags",GetData("MotSet"));
			ClearData("MotSet");
		}
	}
	
	function Freeze()
	{
		if(!IsDataSet("MotSet"))
			SetData("MotSet",GetProperty("MotActorTagList","tags"));
		SetProperty("MotActorTagList","tags","Nothing 0");
	}
	
	function RmvPatrol()
	{
		if(Link.AnyExist("AIPatrol",self))
			Link.DestroyMany("AIPatrol",self,"@Object");

		if(HasProperty("AI_Patrol"))
			SetData("WasPatroling",GetProperty("AI_Patrol"));
		
		if(Object.HasMetaProperty(self,"M-DoesPatrol"))
		{
			SetData("WasMPatroling",true);
			Object.RemoveMetaProperty(self,"M-DoesPatrol");
		}
	
		if(Link.AnyExist("AICurrentPatrol",self))
		{
			local nextTrol = LinkDest(Link.GetOne("AICurrentPatrol",self));
			SendMessage(self,"PreparePatrol",nextTrol);
		}
		
		Property.SetSimple(self,"AI_Patrol",false);
		Link.DestroyMany("AICurrentPatrol",self,"@Object");
	}
	
	function Resurrect()
	{
		Link.DestroyMany("AIWatchObj",self,self);
		Unfreeze();
		
		if(IsDataSet("CurPat"))
		{
			Link.DestroyMany("AICurrentPatrol",self,"@Object");
			Link.DestroyMany("AIPatrol",self,"@Object");
			Link.Create("AIPatrol",self,GetData("CurPat"));
			Link.Create("AICurrentPatrol",self,GetData("CurPat"));
			//DarkUI.TextMessage(GetData("CurPat").tostring() + " : My dest");
			ClearData("CurPat");
		}
		if(IsDataSet("WasMPatroling"))
		{
			Property.Remove(self,"AI_Patrol");
			Object.AddMetaProperty(self,"M-DoesPatrol");
			ClearData("WasMPatroling");
		}
		if(IsDataSet("WasPatroling"))
		{
			SetProperty("AI_Patrol",GetData("WasPatroling"));
			ClearData("WasPatroling");
		}
		
		if(Object.HasMetaProperty(self,"M-ZombiePossum"))
			Object.RemoveMetaProperty(self,"M-ZombiePossum");
		
		if(Object.HasMetaProperty(self,"M-FakeSlainZombie"))
			Object.RemoveMetaProperty(self,"M-FakeSlainZombie");
	}
	
	function OnPreparePatrol()
	{
		//DarkUI.TextMessage((message().data).tostring() + " : My dest");
		if(message().data != null)
			SetData("CurPat",message().data);
		SetData("WasPatroling",true);
	}
	
	function OnMoveAbit()
	{
		Unfreeze();
	}
	
	function OnFreeze()
	{
		Freeze();
	}
	
	function OnBeginScript()
	{
		Physics.SubscribeMsg(self, ePhysScriptMsgType.kCollisionMsg);
		if(Object.HasMetaProperty(self,"M-ZombiePossum") || Object.HasMetaProperty(self,"M-FakeSlainZombie"))
			RmvPatrol();
	}
	function OnEndScript()
	{
		Physics.UnsubscribeMsg(self, ePhysScriptMsgType.kCollisionMsg);
		if(Object.HasMetaProperty(self,"M-ZombiePossum") || Object.HasMetaProperty(self,"M-FakeSlainZombie"))
			RmvPatrol();
	}

   // Problem: will this resurrect the zombie as soon as it dies,
   // due to hitting the player's sword?  Probably ok.
	function OnPhysCollision()
	{
		  local WhatIHit=message().collObj;

		  /*if(WhatIHit!=object("Player"))
			 return;*/

		  Resurrect();
	}
	
	/* Zombies have a NULL slay result, so we can catch the slain event 
      here and decide what to do.  If we _really_ want to slay them, we
      can always resurrect them, set their slay result to terminate, and
      slay them all over again.
   */
	function OnSlain()
	{
		local slay=Property.Get(self,"SlayResult");
		
		//Let's handle the patrols
		//This way efficient zombie won't do their patrol
		//magicaly while we are not looking at them.
		RmvPatrol();
		
		Property.Remove(self,"AI_IdleOrgn");
		if(Property.Possessed(self,"MAX_HP"))
		{
			local max=Property.Get(self,"MAX_HP");
			if(max<=0)
			{
				// set me up to explode
				
				//Old system
				/*local parts=Object.Named("M-ZombieParts");
				
				if(Object.InheritsFrom(self,Object.Named("HammerZombie")))
					parts=Object.Named("M-HamZombieParts");
				Object.AddMetaProperty(self,parts);*/
				
				//We don't want to create billions of archetypes
				//So how about keepon the DelayZomKaboom as single corpse part
				//and create the rest as teleported objects so we could
				//edit them for a better customization.
				local boom = "DelayZomKaboom";
				local parts={
					head 	=	"ZomHead"
					leg		=	"ZomLeg"
					hand	=	"ZomHand"
					arm		=	"ZomArm"
					upperarm=	"ZomUpperArm"
					arm_hand=	"ZomArm+Hand"
					pelv_thi=	"ZomPelvis+Thigh"
					leg_foot=	0
					torso	=	"ZomTorso"
					foot	=	0
					extra0	=	0
					extra1	=	0
					extra2	= 	0
					extra3	=	0
					extra4	= 	0
					extra5	=	0
					extra6	= 	0
					extra7	=	0
					extra8	= 	0
					extra9	= 	0
				};	
				//Let's respect LGS's choices.
				if(Object.InheritsFrom(self,Object.Named("HammerZombie")))
					parts={
					head 	=	"HamZomHead"
					leg		=	"HamZomLeg"
					hand	=	"HamZomHand"
					arm		=	0
					upperarm=	0
					arm_hand=	"HamZomArm+Hand"
					pelv_thi=	"HamZomPelvis+Thigh"
					leg_foot=	"HamZomLeg+Foot"
					torso	=	"HamZomTorso"
					foot	=	0
					extra0	=	0
					extra1	=	0
					extra2	= 	0
					extra3	=	0
					extra4	= 	0
					extra5	=	0
					extra6	= 	0
					extra7	=	0
					extra8	= 	0
					extra9	= 	0
					};
				
				//In case the filthy user want to use a different
				//Kaboom fnord.
				if("boom" in userparams())
					boom = userparams().boom;
				
				//Prefix for Archetype used as a base
				if("prefix" in userparams())
					parts = SetPrefix(parts);
				
				//You want your own archetype per parts?
				//Ok, let's see what you want and fill the gaps
				//with the parts you didn't mentionned settings
				parts = FixParts(parts);
				
				foreach(i,part in parts)
				{
					if(GetObjectId(part) != 0 || ("fillGap" in userparams() && i in def_parts))
					{
						local spart = part;
						
						//Have some 0 you want to be replaced with
						//default limbs?
						if(GetObjectId(part) == 0)
						{
							if(i in def_parts)
								spart = def_parts[i];
						}
						
						//Let's create the limbs
						local o = Object.BeginCreate(GetObjectId(spart));
						
						//CorpseParts are not created at the exact center
						//of the zombie so their limbs fly in all direction
						//Let's emulate that with a tiny random offset
						local randomOffset = vector(Data.RandFltNeg1to1(),Data.RandFltNeg1to1(),Data.RandFlt0to1());
						
						//To prevent as much as possible the limbs to spawn
						//inside walls, let's tweak their offset toward the culprit
						//which has chance to have been not from a wall. 
						local weight = (Object.Position(message().culprit) - Object.Position(self)).GetNormalized() * 0.5;
						randomOffset += weight;
						
						//Let's apply the custom models
						SetModel(o,i);
						SetStr(o,i);
						SetMaterial(o,i);
						SetScale(o,i);
						SetMass(o,i);
						SetGravity(o,i);
						SetRestAxes(o,i);
						
						//This zombie had some Replace texture?
						//Let's tranfer those to limbs so it matches!
						if(HasProperty("OTxtRepr0"))
							Property.SetSimple(o,"OTxtRepr0",GetProperty("OTxtRepr0"));
						if(HasProperty("OTxtRepr1"))
							Property.SetSimple(o,"OTxtRepr1",GetProperty("OTxtRepr1"));
						if(HasProperty("OTxtRepr2"))
							Property.SetSimple(o,"OTxtRepr2",GetProperty("OTxtRepr2"));
						if(HasProperty("OTxtRepr3"))
							Property.SetSimple(o,"OTxtRepr3",GetProperty("OTxtRepr3"));
							
						SetTextures(o,i);
						
						Object.Teleport(o,Object.Position(self)+randomOffset,Object.Facing(self));

						if(!Physics.HasPhysics(o))
						{
							Property.Set(o,"PhysType","Type",1);
							Property.Set(o,"PhysAttr","Gravity %",60);
						}
						Object.EndCreate(o);
					}
				}
				if(GetObjectId(boom) != 0)
						Link.Create("CorpsePart",self,GetObjectId(boom));
					
				Property.SetSimple(self,"SlayResult",eSlayResult.kSlayNormal);
				Sound.HaltSpeech(self);
			}
			else if(slay!=eSlayResult.kSlayNormal)
			{
				// No Data possum is registered, let's check metaprop instead!
				local possum=Object.HasMetaProperty(self,"M-ZombiePossum");
				
				Damage.Resurrect(self);
				
				if(!possum)
				{
				   AI.Signal(self,"Possum");
				}
			}
		}
	}
   
   
	function OnDamage()
	{
		local damage=message().damage;
		local hp=Property.Get(self,"HitPoints");
		local max=hp;
		local stim=message().kind;
		local culprit=message().culprit;
		local permanent=0.0;
	  
		if(culprit != 0 && Link.AnyExist("~CulpableFor",culprit))
			culprit=LinkDest(Link.GetOne("~CulpableFor",culprit));
		
		if(stim==ObjID("HolyStim") ||
			stim==ObjID("FireStim"))
			permanent=1.0;
		else if(stim==ObjID("LightBright"))
			permanent=0.5;

		if(culprit != 0 &&
			 Object.InheritsFrom(culprit,"Hammerite"))
			 permanent=1.0;
		
		//Imagine a world where other AIs can kill zombies like hammers does...
		if("nemesis" in userparams())
		{
			if(typeof userparams().nemesis == "string" || typeof userparams().nemesis == "integer")
			{
				local nemesis_arr = split((userparams().nemesis).tostring(),",");
				foreach(nemesis in nemesis_arr)
				{
					if(GetObjectId(nemesis) != 0 && 
					(Object.InheritsFrom(culprit,nemesis) || culprit == GetObjectId(nemesis)))
						permanent=1.0;
				}
			}
		}
	   
		if(Property.Possessed(self,"MAX_HP"))
		{
			 max=Property.Get(self,"MAX_HP");
			 max-=(damage*permanent);
			 
			 if(Object.HasMetaProperty(self,"M-FakeSlainZombie"))
				max = 0;				
			 
			 Property.SetSimple(self,"MAX_HP",max);
		}
		
		if(Object.HasMetaProperty(self,"M-FakeSlainZombie") || max <= 0)
		{
			Sound.PlaySchemaAtLocation(self,"exp_zombie",Object.Position(self));
			Damage.Slay(self,culprit);
		}

		// Okay, the only way we're going to get damaged by WaterStim
		// is drowning.  If we drown, best to just lie down and stop
		// embarrassing us.
		if(stim==object("WaterStim") && hp<=0)
		{
			Property.SetSimple(self,"SlayResult",eSlayResult.kSlayNormal);
		}
	}
	
	function OnResurrect()
	{
		Resurrect();
	}
	
	function OnPossumMode()
	{

		// Why using lame pseudo script when you can use scripts?
		// Let's check and apply MProps and add link in here!
		
		
		//Let's secure this script : player can destroy it by bullying
		//the zombie so it locked itself into some endless possum mod.
		if(Object.HasMetaProperty(self,"M-FakeSlainZombie"))
				Object.RemoveMetaProperty(self,"M-FakeSlainZombie");
		Sound.HaltSpeech(self);
		
		Freeze();
		
		if(!Object.HasMetaProperty(self,"M-ZombiePossum"))
			Object.AddMetaProperty(self,"M-ZombiePossum");
		Link.DestroyMany("AIWatchObj",self,self);
		Link.Create("AIWatchObj",self,self);
	}
}