Class Tier3Ammo : RandomSpawner2 replaces Shell
{
	Default
	{
		DropItem "BioAmmo", 255, 1;
		DropItem "ShockAmmo", 255, 1;
	}
}
Class Tier3Ammo2 : Tier3Ammo replaces ShellBox {}
Class Tier3Weapon : RandomSpawner2 replaces Shotgun
{
	Default
	{
		DropItem "BioRifle", 255, 1;
		DropItem "ShockRifle", 255, 1;
	}
}
Class Tier3Weapon2 : Tier3Weapon replaces SuperShotgun {}

Class BioAmmo : Ammo
{
	Default
	{
		Tag "Biosludge Ammo";
		Inventory.PickupMessage "You picked up the Biosludge Ammo.";
		Inventory.Amount 25;
		Inventory.MaxAmount 100;
		Ammo.BackpackAmount 50;
		Ammo.BackpackMaxAmount 100;
		Ammo.DropAmount 10;
	}
	States
	{
	Spawn:
		BIOA A -1;
		Stop;
	}
}

Class BioSpark : Actor
{
	Default
	{
		RenderStyle "Add";
		Radius 2;
		Height 0;
		+NOBLOCKMAP;
		+FORCEXYBILLBOARD;
		+MISSILE;
		+MOVEWITHSECTOR;
		+THRUACTORS;
		+ROLLSPRITE;
		+ROLLCENTER;
		+NOTELEPORT;
		BounceType "Doom";
		BounceFactor 0.5;
		WallBounceFactor 0.5;
		Gravity 0.2;
		Scale 0.04;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		frame = Random[GES](0,4);
		roll = FRandom[GES](0,360);
	}
	States
	{
	Spawn:
		GBLB # 1 Bright A_FadeOut(FRandom[GES](0.005,0.015));
		Wait;
	Death:
		GBLB # 1 Bright A_FadeOut(FRandom[GES](0.04,0.06));
		Wait;
	Dummy:
		GBLB ABCDE -1;
		Stop;
	}
}

Class BioHitbox : Actor
{
	Default
	{
		Radius 2;
		Height 2;
		+SHOOTABLE;
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
		+NOBLOOD;
	}
	override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
	{
		if ( inflictor == target ) return 0;
		if ( target && !target.InStateSequence(target.CurState,target.ResolveState("XDeath")) )
		{
			target.bAMBUSH = true;
			target.ExplodeMissile(null,inflictor);
		}
		return 0;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target )
		{
			Destroy();
			return;
		}
		SetOrigin(target.pos-(0,0,height*0.5),true);
	}
}

Class BioLight : DynamicLight
{
	Default
	{
		DynamicLight.Type "Point";
		Args 64,255,48,8;
	}
	override void Tick()
	{
		Super.Tick();
		if ( !target )
		{
			Destroy();
			return;
		}
		args[LIGHT_INTENSITY] = 8*target.Scale.x;
	}
}

Class BioXLight : DynamicLight
{

	double lifetime;
	Default
	{
		DynamicLight.Type "Point";
		Args 64,255,48,30;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		lifetime = 1.0;
	}
	override void Tick()
	{
		Super.Tick();
		if ( globalfreeze || level.frozen ) return;
		args[LIGHT_RED] = 64*lifetime;
		args[LIGHT_GREEN] = 255*lifetime;
		args[LIGHT_BLUE] = 48*lifetime;
		lifetime -= 0.05;
		if ( lifetime <= 0 ) Destroy();
	}
}

Class BioGel : Actor
{
	Actor l, b;
	enum EHitType
	{
		HIT_NONE,
		HIT_WALL,
		HIT_CEILING,
		HIT_FLOOR
	};
	int hittype;
	int deadtimer;
	Line atline;
	int atside;
	int rollvel, pitchvel, yawvel;

	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		vel.z += 6;
		deadtimer = -1;
		l = Spawn("BioLight",pos);
		l.target = self;
		b = Spawn("BioHitbox",pos);
		b.target = self;
		rollvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
		pitchvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
		yawvel = FRandom[GES](10,30)*RandomPick[GES](-1,1);
	}
	override int SpecialMissileHit( Actor victim )
	{
		if ( victim == b ) return 1;
		if ( (victim is 'BioHitbox') && ((victim.target == master) || (victim.target.master == master)) ) return 1;
		return -1;
	}
	override void Tick()
	{
		Super.Tick();
		if ( globalfreeze || level.frozen ) return;
		if ( !bNOGRAVITY )
		{
			A_SetRoll(roll+rollvel,SPF_INTERPOLATE);
			A_SetPitch(pitch+pitchvel,SPF_INTERPOLATE);
			A_SetPitch(pitch+yawvel,SPF_INTERPOLATE);
			if ( waterlevel > 0 )
			{
				vel.xy *= 0.98;
				rollvel *= 0.98;
				pitchvel *= 0.98;
				yawvel *= 0.98;
			}
		}
		if ( !InStateSequence(CurState,FindState("XDeath")) && ((!bNOGRAVITY && !Random[GES](0,2)) || !Random[GES](0,10)) )
		{
			int numpt = Min(20,Scale.x*2)+Random[GES](-1,1);
			for ( int i=0; i<numpt; i++ )
			{
				Vector3 pvel = (FRandom[GES](-1,1),FRandom[GES](-1,1),FRandom[GES](-1,1)).unit()*FRandom[GES](1,3);
				let s = Spawn("BioSpark",pos);
				s.vel = pvel;
			}
		}
		if ( deadtimer <= -1 ) return;
		let bi = BlockThingsIterator.Create(self,4*Scale.x);
		while ( bi.Next() )
		{
			if ( !bi.Thing || !bi.Thing.bSHOOTABLE || (bi.Thing == b) || ((bi.Thing is 'BioHitbox') && ((bi.Thing.target.master == self) || (bi.Thing.target == master))) ) continue;
			if ( (Distance2D(bi.Thing)-bi.Thing.radius <= 4*Scale.x) && ((bi.Thing.pos.z <= pos.z+4*Scale.x) && (bi.Thing.pos.z+bi.Thing.height >= pos.z-4*Scale.x)) ) deadtimer = 0;
		}
		if ( deadtimer-- <= 0 )
		{
			deadtimer = -1;
			SetStateLabel("XDeath");
		}
	}
	// align self to what surface was hit, currently does not support 3d floors + slopes properly
	virtual void AlignSelf()
	{
		A_NoGravity();
		A_Stop();
		if ( bAMBUSH )
		{
			SetStateLabel("XDeath");
			return;
		}
		Vector3 normal = (0,0,0);
		FLineTraceData d;
		A_SetSize(0.1,0);
		if ( BlockingLine )
		{
			atline = BlockingLine;
			normal = (-BlockingLine.delta.y,BlockingLine.delta.x,0).unit();
			atside = 1;
			if ( !BlockingLine.sidedef[1] || (CurSector == BlockingLine.frontsector) )
			{
				atside = 0;
				normal *= -1;
			}
			Vector3 orig = (BlockingLine.v1.p.x,BlockingLine.v1.p.y,0);
			Vector3 onwall = pos-(normal dot (pos-orig))*normal;
			SetOrigin(onwall+normal*0.5,false);
			angle = atan2(normal.y,normal.x);
			pitch = 0;
			roll = 180;	// otherwise it slides upwards (UT changes roll like this too)
			if ( waterlevel > 0 ) hittype = HIT_FLOOR;
			else hittype = HIT_WALL;
		}
		else if ( pos.z <= floorz+4 )
		{
			normal = cursector.floorplane.Normal;
			pitch = asin(-normal.z);
			angle = atan2(normal.y,normal.x);
			roll = FRandom[GES](0,360);
			SetOrigin((pos.x,pos.y,floorz)+normal*0.5,false);
			hittype = HIT_FLOOR;
		}
		else if ( pos.z >= ceilingz-8 )
		{
			normal = cursector.ceilingplane.Normal;
			pitch = asin(-normal.z);
			angle = atan2(normal.y,normal.x);
			roll = FRandom[GES](0,360);
			SetOrigin((pos.x,pos.y,ceilingz)+normal*0.5,false);
			if ( waterlevel > 0 ) hittype = HIT_FLOOR;
			else if ( normal dot (0,0,-1) > 0.7 )
				hittype = HIT_CEILING;
			else hittype = HIT_FLOOR;
		}
		else
		{
			SetStateLabel("XDeath");
			return;
		}
		A_PlaySound("ges/hit");
		A_SprayDecal("BioSplat",-172);
		int numpt = Min(100,Scale.x*10)+Random[GES](-5,5);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (normal+(FRandom[GES](-.8,.8),FRandom[GES](-.8,.8),FRandom[GES](-.8,.8))).unit()*FRandom[GES](3,6);
			let s = Spawn("BioSpark",pos);
			s.vel = pvel;
		}
	}
	action void A_DropDrip()
	{
		let d = Spawn("BioSplash",pos+cursector.ceilingplane.Normal*2*scale.x);
		d.target = target;
		d.angle = angle;
		d.pitch = pitch;
		d.roll = roll;
		d.master = self;
		d.scale = scale*0.5;
		d.vel.z -= 6;
	}
	action void A_GelExplode()
	{
		A_NoGravity();
		A_Stop();
		if ( invoker.l ) invoker.l.Destroy();
		if ( invoker.b ) invoker.b.Destroy();
		let s = Spawn("BioXLight",pos);
		s.args[3] *= Scale.x;
		invoker.deadtimer = -1;
		if ( invoker.atline ) invoker.atline.RemoteActivate(target,invoker.atside,SPAC_Impact,pos);
		A_Explode(Random[GES](15,25)*Scale.x,Min(250,Scale.x*25));
		A_PlaySound("ges/explode",CHAN_VOICE);
		int numpt = Min(300,Scale.x*30)+Random[GES](-10,10);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[GES](-1,1),FRandom[GES](-1,1),FRandom[GES](-1,1)).unit()*FRandom[GES](3,12);
			let s = Spawn("BioSpark",pos);
			s.vel = pvel;
		}
		Scale *= 0.5;
	}
	Default
	{
		Obituary "%o drank a glass of %k's dripping green load.";
		DamageType 'Slime';
		RenderStyle "Add";
		Radius 4;
		Height 4;
		Scale 2;
		Speed 20;
		PROJECTILE;
		-NOGRAVITY;
		+SKYEXPLODE;
		+FORCERADIUSDMG;
		+FORCEXYBILLBOARD;
		+MOVEWITHSECTOR;
	}
	States
	{
	Spawn:
		GELF ABCDEFGHIJKLM 1 Bright;
		Loop;
	Death:
		GELH A 1 Bright AlignSelf();
		GELH BCDEFGHIJ 1 Bright;
		GELH J 1 Bright A_SetTics(Random[GES](10,30));
		GELH J -1 Bright
		{
			invoker.deadtimer = Random[GES](250,300);
			if ( invoker.hittype == HIT_WALL ) return ResolveState("Slide");
			else if ( invoker.hittype == HIT_CEILING ) return ResolveState("Drip");
			return ResolveState(null);
		}
		Stop;
	Drip:
		GELD ABCDEFGH 4 Bright;
		GELD I 4 Bright A_DropDrip();
		GELD JKLM 4 Bright;
		GELH J -1 Bright;
		Stop;
	Slide:
		GELS ABCDEF 3 Bright;
		GELS G -1 Bright;
		Stop;
	Crash:
	XDeath:
		#### # 1 Bright A_SetTics(Random[GES](1,3));
		GEX1 A 1 Bright A_GelExplode();
		GEX1 BCDEFGHIJK 3 Bright;
		Stop;
	Shrivel:
		GELX ABCDEFGHIJKL 1 Bright;	// UNUSED
		Stop;
	}
}

Class BioGlob : BioGel
{
	int numsplash;

	override void AlignSelf()
	{
		Super.AlignSelf();
		if ( !bAMBUSH ) numsplash = 2*Scale.x-1;
	}
	override void Tick()
	{
		Super.Tick();
		if ( globalfreeze || level.frozen ) return;
		Vector3 ofs = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
		for ( int i=0; i<2; i++ )
		{
			if ( numsplash-- <= 0 ) return;
			Vector3 dir = (ofs+(FRandom[GES](-.8,.8),FRandom[GES](-.8,.8),FRandom[GES](-.8,.8))).unit();
			let d = Spawn("BioSplash",pos+ofs*4);
			d.target = target;
			d.master = self;
			d.scale *= FRandom[GES](0.5,0.7);
			d.angle = atan2(dir.y,dir.x);
			d.pitch = -asin(dir.z);
			d.vel = (cos(d.angle)*cos(d.pitch),sin(d.angle)*cos(d.pitch),-sin(d.pitch))*d.speed*FRandom[GES](0.25,0.35);
			d.vel.z -= 3;
		}
	}
}

Class BioSplash : BioGel
{
	override void AlignSelf()
	{
		Super.AlignSelf();
		if ( hittype == HIT_CEILING ) hittype = HIT_FLOOR;
	}
}

Class BioRifle : UTWeapon
{
	double charge;

	action void A_BioFire( bool alt = false )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( !alt )
		{
			if ( weap.Ammo1.Amount <= 0 ) return;
			if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		}
		A_PlaySound("ges/fire",CHAN_WEAPON);
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(48,0,255,0),1);
		A_AlertMonsters();
		if ( alt ) A_QuakeEx(1+0.5*invoker.charge,1+0.5*invoker.charge,1+0.5*invoker.charge,5+1.2*invoker.charge,0,64,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05+0.01*invoker.charge);
		else A_QuakeEx(1,1,1,5,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.05);
		Vector3 x, y, z;
		double a, s;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+8.0*y-5.0*z;
		Actor p;
		if ( alt )
		{
			p = Spawn("BioGlob",origin);
			p.A_SetScale(1+0.8*invoker.charge);
		}
		else p = Spawn("BioGel",origin);
		p.angle = angle;
		p.pitch = BulletSlope();
		p.vel = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch))*p.speed;
		p.target = self;
	}
	action void A_BeginCharge()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		invoker.charge = 0;
		A_PlaySound("ges/charge",CHAN_WEAPON);
	}
	action void A_ChargeUp()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( invoker.charge >= 5.1 ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		invoker.charge = min(5.1,invoker.charge+0.5);
	}
	Default
	{
		Tag "GES Bio Rifle";
		Inventory.PickupMessage "You got the GES BioRifle.";
		Weapon.UpSound "ges/select";
		Weapon.SlotNumber 3;
		Weapon.SelectionOrder 7;
		Weapon.AmmoType "BioAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "BioAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 25;
	}
	States
	{
	Spawn:
		BIOP A -1;
		Stop;
		BIOP B -1;
		Stop;
	Ready:
		BIOS ABCDEFGHIJKLMNOPQRSTUV 1;
	Idle:
		BIOI A 1
		{
			A_CheckReload();
			A_WeaponReady();
		}
		Goto Idle;
	Fire:
		BIOF A 1 A_BioFire();
		BIOF BCDEFGHI 1;
		Goto Idle;
	AltFire:
		BIOC A 4 A_BeginCharge();
		BIOC B 5 A_ChargeUp();
		BIOC CD 5;
		BIOC E 0 A_Refire("AltFire2");
		Goto AltRelease;
	AltFire2:
		BIOC E 5 A_ChargeUp();
		BIOC FG 5;
		BIOC H 0 A_Refire("AltFire3");
		Goto AltRelease;
	AltFire3:
		BIOC H 5 A_ChargeUp();
		BIOC IJ 5;
		BIOC K 0 A_Refire("AltFire4");
		Goto AltRelease;
	AltFire4:
		BIOC K 5 A_ChargeUp();
		BIOC LM 5;
		BIOC N 0 A_Refire("AltFire5");
		Goto AltRelease;
	AltFire5:
		BIOC N 5 A_ChargeUp();
		BIOC OP 5;
		BIOC Q 0 A_Refire("AltFire6");
		Goto AltRelease;
	AltFire6:
		BIOC Q 5 A_ChargeUp();
		BIOC RS 5;
		BIOC T 0 A_Refire("AltFire7");
		Goto AltRelease;
	AltFire7:
		BIOC T 5 A_ChargeUp();
		BIOC UV 5;
		BIOC W 0 A_Refire("AltFire8");
		Goto AltRelease;
	AltFire8:
		BIOC W 5 A_ChargeUp();
		BIOC XY 5;
		BIOC Z 0 A_Refire("AltFire9");
		Goto AltRelease;
	AltFire9:
		BIOC Z 5 A_ChargeUp();
		BIC2 AB 5;
		BIC2 C 0 A_Refire("AltFire10");
		Goto AltRelease;
	AltFire10:
		BIC2 C 5 A_ChargeUp();
		BIC2 DE 5;
		BIOM A 0 A_Refire("AltHeld");
		Goto AltRelease;
	AltHeld:
		BIOM A 5 { invoker.charge = min(5.1,invoker.charge+0.1); }
		BIOM A 0 A_Refire("AltHeld");
	AltRelease:
		BIOE A 1;
		BIOF A 2 A_BioFire(true);
		BIOF BCDEFGHI 2;
		Goto Idle;
	Select:
		BIOS A 1 A_Raise(int.max);
		Wait;
	Deselect:
		BIOD ABCDEFGHIJ 1;
		BIOD J 1 A_Lower(int.max);
		Wait;
	}
}