version "3.3.0"

class UGGibHandler : EventHandler
{
	override void WorldThingDamaged(WorldEvent e)
	{
		//Enabling switches.
		bool UseBloodColor = CVar.GetCVar("ug_bloodcolor").GetBool();
		bool ScaleGibs = CVar.GetCVar("ug_scale_gibs").GetBool();
		
		//Misc stuff.
		int OverkillLevel = CVar.GetCVar("ug_overkill").GetInt();
		double GibThreshold = CVar.GetCVar("ug_threshold").GetFloat();
		double Brightness = CVar.GetCVar("ug_blood_brightness").GetFloat();
		
		//Gib mode.
		int GibMode = CVar.GetCVar("ug_gib_mode").GetInt();
		
		//Velocities.
		double VelMultXY = CVar.GetCVar("ug_gibs_velocity_xy").GetFloat();
		double VelMultZ = CVar.GetCVar("ug_gibs_velocity_z").GetFloat();
		
		//Some variables used by all checks.
		Color BloodCol = Color(int(e.Thing.bloodcolor.r * Brightness), int(e.Thing.bloodcolor.g * Brightness), int(e.Thing.bloodcolor.b * Brightness));
		Color DefCol = Color(int(256 * Brightness), 0, 0);
		
		bool success;
		Actor item;
	
		//Main script that handles everything.
		if (CVar.GetCVar("ug_gibs").GetBool() && e.Thing.bIsMonster && !e.Thing.bNOBLOOD && (e.Inflictor ? !e.Inflictor.bNOEXTREMEDEATH : true) && e.DamageType != "Ice" && e.Damage < 1000000 && e.Thing.Health <= (0 - e.Thing.SpawnHealth() / 12 - 10) * GibThreshold)
		{
			e.Thing.A_PlaySound("UniversalGibs/Gib", CHAN_AUTO);
			
			int GibMult = 1; //Overkill? Nah.
			if (OverkillLevel > 1 && e.Thing.Health <= (0 - e.Thing.SpawnHealth() / 10 - 20) * GibThreshold) GibMult++; //More gibs.
			if (OverkillLevel > 2 && e.Thing.Health <= (0 - e.Thing.SpawnHealth() / 8 - 40) * GibThreshold) GibMult++; //All the gibs.
			if (OverkillLevel > 3 && e.Thing.Health <= (0 - e.Thing.SpawnHealth() / 6 - 80) * GibThreshold) GibMult++; //Brutal Doom's got nothing on this.
			
			e.Thing.SetShade(UseBloodColor && BloodCol ? BloodCol : DefCol);
			
			//Corpse.
			[success, item] = e.Thing.A_SpawnItemEx("UGGib_Corpse", flags: (UseBloodColor && BloodCol ? SXF_USEBLOODCOLOR : 0) | SXF_TRANSFERSTENCILCOL);
			if (success) item.A_SetScale(min(0.5 + e.Thing.Radius / 32.0, 2.50));
			
			//Gibs.
			float GibMultVel = (GibMult + 1) * 0.5;
			double InflictorAngle = e.DamageSource ? (e.DamageSource.AngleTo(e.Thing, true) + 45) : 0;
			bool FaceInflictor = CVar.GetCVar("ug_advphysics").GetBool() && e.DamageSource;
			int DefHeight = e.Thing.Default.Height;
			
			for (int i = 0; i < 1 * GibMult; ++i)
			{
				switch (GibMode)
				{
					case 0: //Default mode.
						for (int j = 0; j <= 9; ++j)
						{
							double xoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double yoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double xvel = frandom(2.0, 8.0) * VelMultXY * GibMultVel;
							double yvel = (FaceInflictor ? frandom(2.0, 8.0) : 0) * VelMultXY * GibMultVel;
							double zvel = frandom(2.0, FaceInflictor ? 5.0 : 8.0) * VelMultZ * GibMultVel;
							double angle = FaceInflictor ? InflictorAngle : random(0, 360);
							[success, item] = e.Thing.A_SpawnItemEx("UGGib_Obj_Def"..j, xoff, yoff, random(DefHeight * 0.25, DefHeight * 0.75), xvel, yvel, zvel, angle, (UseBloodColor && BloodCol ? SXF_USEBLOODCOLOR : 0) | SXF_TRANSFERSTENCILCOL | SXF_ABSOLUTEANGLE | SXF_ABSOLUTEPOSITION);	
							if (success && ScaleGibs) item.A_SetScale(min(0.5 + e.Thing.Radius / 32.0, 2.25));
						}
						break;
						
					case 1: //Mortal Kombat mode.
						for (int j = 0; j <= 5; ++j) //Skulls and ribcages.
						{
							double xoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double yoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double xvel = (FaceInflictor ? frandom(4.0, 14.0) : frandom(0.0, 3.5)) * VelMultXY;
							double yvel = (FaceInflictor ? frandom(4.0, 14.0) : 0) * VelMultXY;
							double zvel = (FaceInflictor ? frandom(4.0, 16.0) : frandom(5.0, 22.0)) * VelMultZ;
							double angle = FaceInflictor ? InflictorAngle : random(0, 360);
							[success, item] = e.Thing.A_SpawnItemEx("UGGib_Obj_MK"..(j % 2), xoff, yoff, random(DefHeight * 0.25, DefHeight * 0.75), xvel, yvel, zvel, angle, SXF_ABSOLUTEANGLE | SXF_ABSOLUTEPOSITION);	
							if (success && ScaleGibs) item.A_SetScale(min(0.4 + e.Thing.Radius / 128.0, 2.25));
						}
						for (int j = 0; j <= 12; ++j) //Bones.
						{
							double xoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double yoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double xvel = (FaceInflictor ? frandom(4.0, 14.0) : frandom(0.0, 3.5)) * VelMultXY;
							double yvel = (FaceInflictor ? frandom(4.0, 14.0) : 0) * VelMultXY;
							double zvel = (FaceInflictor ? frandom(4.0, 16.0) : frandom(5.0, 22.0)) * VelMultZ;
							double angle = FaceInflictor ? InflictorAngle : random(0, 360);
							[success, item] = e.Thing.A_SpawnItemEx("UGGib_Obj_MK2", xoff, yoff, random(DefHeight * 0.25, DefHeight * 0.75), xvel, yvel, zvel, angle, SXF_ABSOLUTEANGLE | SXF_ABSOLUTEPOSITION);	
							if (success && ScaleGibs) item.A_SetScale(min(0.5 + e.Thing.Radius / 48.0, 2.25));
						}
						break;
						
					case 2: //Cogs 'n' Springs mode.
						for (int j = 0; j <= 11; ++j)
						{
							double xoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double yoff = random(-e.Thing.Radius, e.Thing.Radius) * 0.5;
							double xvel = frandom(2.0, 5.0) * VelMultXY * GibMultVel;
							double yvel = (FaceInflictor ? frandom(2.0, 5.0) : 0) * VelMultXY * GibMultVel;
							double zvel = frandom(2.0, 5.0) * VelMultZ * GibMultVel;
							double angle = FaceInflictor ? InflictorAngle : random(0, 360);
							[success, item] = e.Thing.A_SpawnItemEx("UGGib_Obj_NNS"..(j % 2), xoff, yoff, random(DefHeight * 0.25, DefHeight * 0.75), xvel, yvel, zvel, angle, SXF_ABSOLUTEANGLE | SXF_ABSOLUTEPOSITION);	
							if (success && ScaleGibs) item.A_SetScale(min(0.1 + e.Thing.Radius / 128.0, 0.90));
						}
						break;
				}
			}
				
			e.Thing.A_GiveInventory("UGCorpseCleaner"); //Destroys actor once it reaches a state with infinite duration.
			e.Thing.A_ChangeLinkFlags(1, true); //Sets NOSECTOR.
			e.Thing.bICECORPSE = true; //Prevents resurrecting of any form.
		}
		
		//If corpse isn't gibbed and shootable corpses is enabled, toggle shootable behaviour.
		if (CVar.GetCVar("ug_gibs").GetBool() && CVar.GetCVar("ug_corpseshoot").GetBool() && e.Thing.bIsMonster && !e.Thing.bNOBLOOD && e.Thing.Health <= 0 && !e.Thing.CheckInventory("UGCorpseCleaner", 0))
		{
			e.Thing.A_GiveInventory("UGMakeShootableCorpse");
		}
	}
}

// ------------- DEFAULT GIBS -------------

class UGGibBase : Actor
{
	override void BeginPlay()
	{
		IsSliding = false;
		TrailSpawnDuration = 25;
		SlideTrailTicker = 0;
		BouncesMade = 0;
		
		Super.BeginPlay();
	}
	
	override void Tick()
	{
		if (!bNOBLOOD && CVar.GetCVar("ug_gibs_trails").GetBool())
		{
			if (--TrailSpawnDuration > 0)
			{
				if (!random(0, 4 - CVar.GetCVar("ug_gibs_trail_density").GetInt()))
					A_SpawnParticle(fillcolor, SPF_RELATIVE, 40, GetCVar("ug_gibs_trail_size"), 0, 0, 0, 0, 0, 0, 2, 0, 0, -0.2, 1.0, -1, 0.5);
			}
			
			if (--SlideTrailTicker <= 0 && IsSliding)
			{
				A_SpawnParticle(fillcolor, SPF_RELATIVE, 70, GetCVar("ug_gibs_trail_size"));
				SlideTrailTicker = 2; //Don't always spawn trails.
			}
		}
		
		Super.Tick();
	}
	
	bool IsSliding;
	int TrailSpawnDuration;
	int SlideTrailTicker;
	int BouncesMade;
	
	Default
	{
		Radius 3;
		Height 4;
		+MISSILE
		+NOBLOCKMAP
		+ROLLSPRITE
		+ROLLCENTER
		+USEBOUNCESTATE
		+MOVEWITHSECTOR
		+NOTELEPORT
		+THRUACTORS
		BounceType "Hexen";
		BounceCount 4;
		BounceFactor 0.40;
	}
	
	States
	{
		Spawn:
			#### A 2 A_SetRoll(roll + random(10, 20));
			Loop;
		Sliding:
			#### A -1;
			Stop;
		Bounce.Wall:
			#### A 0
			{
				if (!random(0, 1) && !bNOBLOOD && GetCVar("ug_gibs_wallslide")) //50% chance to stick to wall. Can't stick if there's no blood.
				{
					IsSliding = true;
					bNOGRAVITY = true;
					A_SetRoll(randompick(-90, 90));
					A_ChangeVelocity(0, 0, frandom(-4.0, -1.0), CVF_REPLACE);
					
					return ResolveState("Sliding");
				}
				
				return ResolveState(null);
			}
			Goto Spawn;
		Bounce.Floor:
			#### A 0
			{
				if (!bNOBLOOD)
				{
					switch (GetCVar("ug_gibs_floor"))
					{
						case 1: if (BouncesMade++ < 1) A_SpawnItemEx("UGGib_Blood_Floor", 2, 0, floorz - pos.z, 0, 0, 0, 0, SXF_TRANSFERSTENCILCOL | SXF_TRANSFERSCALE); break;
						case 2: A_SpawnItemEx("UGGib_Blood_Floor", 2, 0, floorz - pos.z, 0, 0, 0, 0, SXF_TRANSFERSTENCILCOL | SXF_TRANSFERSCALE); break;
					}
				}
				
				if (IsSliding)
				{
					IsSliding = false;
					A_ChangeVelocity(0, 0, 0, CVF_REPLACE);
					return ResolveState("Death");
				}
				
				return ResolveState(null);
			}
			Goto Spawn;
		Death:
			#### A 35
			{
				int LifeTime = 35 * GetCVar("ug_lifetime");
				A_SetTics(LifeTime > 0 ? LifeTime : -1);
				
				A_SetRoll(randompick(0, 180) + random(-5, 5));
			}
		FadeOut:
			#### A 1 A_FadeOut(0.025);
			Loop;
	}
}

class UGGib_Obj_Def0 : UGGibBase {States { Spawn: GIB0 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def1 : UGGibBase {States { Spawn: GIB1 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def2 : UGGibBase {States { Spawn: GIB2 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def3 : UGGibBase {States { Spawn: GIB3 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def4 : UGGibBase {States { Spawn: GIB4 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def5 : UGGibBase {States { Spawn: GIB5 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def6 : UGGibBase {States { Spawn: GIB6 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def7 : UGGibBase {States { Spawn: GIB7 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def8 : UGGibBase {States { Spawn: GIB8 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_Def9 : UGGibBase {States { Spawn: GIB9 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }

// ------------- MORTAL KOMBAT GIBS -------------

class UGGib_Obj_MK0 : UGGibBase { Default { Scale 0.60; BounceFactor 0.60; +NOBLOOD } States { Spawn: MKB0 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_MK1 : UGGibBase { Default { Scale 0.60; BounceFactor 0.60; +NOBLOOD } States { Spawn: MKB1 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_MK2 : UGGibBase { Default { Scale 0.75; BounceFactor 0.60; +NOBLOOD } States { Spawn: MKB2 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }

// ------------- COGS 'N' SPRINGS -------------

//It's "cogs", not "nuts". I'm a dumbass. Kept the classes named the same way for compatibility reasons.
class UGGib_Obj_NNS0 : UGGibBase { Default { Scale 0.20; BounceFactor 0.60; +NOBLOOD } States { Spawn: NTS0 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }
class UGGib_Obj_NNS1 : UGGibBase { Default { Scale 0.20; BounceCount 10; BounceFactor 0.80; +NOBLOOD } States { Spawn: NTS1 A 2 A_SetRoll(roll + random(10, 20)); Loop; } }

// ------------- MISC STUFF -------------

class UGGib_Floor_Base : Actor
{
	bool CheckWaterTexture()
	{
		static const string WaterTextures[] =
		{ 
			"NUKAGE1",  "NUKAGE2",  "NUKAGE3", 
			"BLOOD1",  "BLOOD2",  "BLOOD3", 
			"SLIME01",  "SLIME02",  "SLIME03",  "SLIME04",  "SLIME05",  "SLIME06",  "SLIME07",  "SLIME08", 
			"LAVA1",  "LAVA2",  "LAVA3",  "LAVA4", 
			"FWATER1",  "FWATER2",  "FWATER3",  "FWATER4", 
			"F_HWATR1",  "F_HWATR2",  "F_HWATR3",
			"F_PWATR1",  "F_PWATR2",  "F_PWATR3",
			"P_VWATR1",  "P_VWATR2",  "P_VWATR3",
			"F_WATR01",  "F_WATR02",  "F_WATR03", 
			"FLATHUH1",  "FLATHUH2",  "FLATHUH3",  "FLATHUH4", 
			"FLTFLWW1",  "FLTFLWW2",  "FLTFLWW3",
			"FLTWAWA1",  "FLTWAWA2",  "FLTWAWA3",
			"FLTLAVA1",  "FLTLAVA2",  "FLTLAVA3", "FLTLAVA4",
			"FLTSLUD1",  "FLTSLUD2",  "FLTSLUD3", 
			"X_001", "X_002", "X_003", "X_004", "X_005", "X_006", "X_007", "X_008", "X_009", "X_010", "X_011" 
		};
		
		string FTex = TexMan.GetName(floorpic);
				
		for (int i = 0; i < WaterTextures.Size(); ++i)
			if (FTex == WaterTextures[i])
				return true;
			
		return false;
	}
	
	override void PostBeginPlay()
	{
		Alpha = CVar.GetCVar("ug_gibs_floor_alpha").GetFloat();
		
		Super.PostBeginPlay();
	}
	
	override void Tick()
	{
		//If decal is not fading in any way.
		if (!InStateSequence(curState, ResolveState("FadeOut")) && !InStateSequence(curState, ResolveState("OnWater")))
			Alpha = CVar.GetCVar("ug_gibs_floor_alpha").GetFloat();
		
		Super.Tick();
	}
	
	Default
	{
		Radius 2;
		+NOINTERACTION
		+MOVEWITHSECTOR
		+FLATSPRITE
	}
	
	States
	{
		Spawn:
			#### A 35
			{
				bool Dissolve =  CVar.GetCVar("ug_dissolve").GetBool();
				bool Disappear = CVar.GetCVar("ug_bloodfade").GetBool();
				bool ScaleGibs = CVar.GetCVar("ug_scale_gibs").GetBool();
				
				int LifeTime = 35 * GetCVar("ug_lifetime");
				A_SetTics(LifeTime > 0 && Disappear ? LifeTime : -1);
				
				//Use old scaling formula if size scaling is disabled.
				A_SetScale(ScaleGibs ? Scale.X - frandom(0.30, 1.00) : frandom(0.25, 0.6)); 
				
				A_SetAngle(random(0, 360));
				
				if (Dissolve && ((CheckWaterTexture() && pos.z - floorz == 0) || waterlevel > 0))
				{
					Alpha = CVar.GetCVar("ug_gibs_floor_alpha").GetFloat() * frandom(0.40, 0.80);
					A_SetScale(Scale.X * 0.50);
					return ResolveState("OnWater");
				}
				return ResolveState(null);
			}
		FadeOut:
			#### A 1 {A_FadeOut(0.025); bnoclip = true; bnointeraction = true;}
			Loop;
		OnWater:
			#### B 1
			{
				A_SetScale(Scale.X + 0.0012);
				A_FadeOut(0.0005);
			}
			Loop;
	}
}

class UGGib_Blood_Floor : UGGib_Floor_Base { Default { RenderStyle "Shaded"; } States { Spawn: BLDS A 0; Goto Super::Spawn; } }

//---------
// CORPSE
//---------

class UGGib_Corpse : Actor
{	
	Default
	{
		Radius 6;
		+CORPSE
		+NOBLOCKMAP
		+MOVEWITHSECTOR
	}
	
	States
	{
		Spawn:
			CRS1 A 35 NoDelay
			{
				bool Disappear = CVar.GetCVar("ug_corpsefade").GetBool();
				
				int LifeTime = 35 * GetCVar("ug_lifetime");
				A_SetTics(LifeTime > 0 && Disappear ? LifeTime : -1);
			}
		FadeOut:
			CRS1 A 1 A_FadeOut(0.025);
			Loop;
	}
}

class UGGib_Corpse_Shootable : Actor
{	
	void A_GibCorpse()
	{
		bool ScaleGibs = CVar.GetCVar("ug_scale_gibs").GetBool();
		int GibMode = CVar.GetCVar("ug_gib_mode").GetInt();
		double VelMultXY = CVar.GetCVar("ug_gibs_velocity_xy").GetFloat();
		double VelMultZ = CVar.GetCVar("ug_gibs_velocity_z").GetFloat();
		
		double ScaleVelMult = 1.0 + tracer.Radius * 0.02;
			
		A_PlaySound("UniversalGibs/Gib", CHAN_AUTO);
		
		switch (GibMode)
		{
			case 0: //Default mode.
				for (int j = 0; j <= 18; ++j)
					A_SpawnItemEx("UGGib_Obj_Def"..(j % 10), 0, 0, 2, frandom(2.0, 8.0) * VelMultXY * ScaleVelMult, 0, frandom(2.0, 8.0) * VelMultZ * ScaleVelMult, random(0, 360), SXF_TRANSFERTRANSLATION | SXF_TRANSFERSTENCILCOL | (ScaleGibs ? SXF_TRANSFERSCALE : 0));	
				break;
				
			case 1: //Mortal Kombat mode.
				for (int j = 0; j <= 5; ++j) //Skulls and ribcages.
					A_SpawnItemEx("UGGib_Obj_MK"..(j % 2), 0, 0, 2, frandom(0.0, 3.5) * VelMultXY, 0, frandom(5.0, 22.0) * VelMultZ, random(0, 360), (ScaleGibs ? SXF_TRANSFERSCALE : 0));	
					
				for (int j = 0; j <= 15; ++j) //Bones.
					A_SpawnItemEx("UGGib_Obj_MK2", 0, 0, 2, frandom(0.0, 3.5) * VelMultXY, 0, frandom(5.0, 22.0) * VelMultZ, random(0, 360), (ScaleGibs ? SXF_TRANSFERSCALE : 0));	
				break;
				
			case 2: //Cogs 'n' Springs mode.
				for (int j = 0; j <= 11; ++j)
					A_SpawnItemEx("UGGib_Obj_NNS"..(j % 2), 0, 0, 2, frandom(2.0, 5.0) * VelMultXY * ScaleVelMult, 0, frandom(2.0, 5.0) * VelMultZ * ScaleVelMult, random(0, 360), (ScaleGibs ? SXF_TRANSFERSCALE : 0));
				break;
		}
	}

	override void PostBeginPlay()
	{
		if (tracer)
		{
			Health = 3 * tracer.Radius * CVar.GetCVar("ug_corpsehealth").GetFloat();
			A_SetSize(tracer.Radius, tracer.Height);
		}
		else A_Log("Tracer is null! Called from UGGib_Corpse_Shootable -> PostBeginPlay.");
		
		Super.PostBeginPlay();
	}

	override void Tick()
	{
		if(!tracer || tracer.health > 0)
		{
			Destroy();
			Return;
		}
		
		bSHOOTABLE = CVar.GetCVar("ug_corpseshoot").GetBool();
		A_ChangeLinkFlags(!bSHOOTABLE, false);
		A_Warp(AAPTR_TRACER, 0, 0, 0, 0, WARPF_NOCHECKPOSITION | WARPF_INTERPOLATE);

		Super.Tick();
	}
	
	Default
	{
		+NOBLOCKMAP
		+MOVEWITHSECTOR
		+DONTTHRUST
		+NOKILLSCRIPTS
		+NOTAUTOAIMED
	}

	States
	{
		Spawn:
			TNT1 A -1;
			Stop;
		Death:
			TNT1 A 1
			{
				A_GibCorpse();
				tracer.Destroy();
			}
			Stop;
	}
}

// ------------- EXTRA MISC STUFF -------------

class UGCorpseCleaner : Inventory //This one takes care of most invisible corpses.
{
	override void DoEffect()
	{
		if (owner.tics == -1) owner.Destroy();
			
		Super.DoEffect();
	}
}

class UGMakeShootableCorpse : Inventory //This one makes corpses shootable and stuff.
{
	override void DoEffect()
	{
		if (owner.tics == -1)
		{
			bool UseBloodColor = CVar.GetCVar("ug_bloodcolor").GetBool();
			double Brightness = CVar.GetCVar("ug_blood_brightness").GetFloat();
			
			Color BloodCol = Color(int(owner.bloodcolor.r * Brightness), int(owner.bloodcolor.g * Brightness), int(owner.bloodcolor.b * Brightness));
			Color DefCol = Color(int(256 * Brightness), 0, 0);
			owner.SetShade(UseBloodColor && bloodCol ? bloodCol : defCol);

			bool success;
			Actor item;
			[success, item] = owner.A_SpawnItemEx("UGGib_Corpse_Shootable", flags: (UseBloodColor && bloodCol ? SXF_USEBLOODCOLOR : 0) | SXF_TRANSFERSTENCILCOL);
			if (success)
			{
				item.A_SetScale(min(0.5 + owner.Radius / 32.0, 2.25));
				item.tracer = owner;
			}

			GoAwayAndDie();
		}

		Super.DoEffect();
	}
}

class sp4wner : actor
{
states
{
spawn:
	tnt1 q 34 spawn("doomimp", self.pos + (frandom(-100, 100), frandom(-100, 100), 0) );
loop;
}

}