Class HammerImpact : Actor
{
	Default
	{
		Radius 0.1;
		Height 0;
		+NOGRAVITY;
		+NOCLIP;
		+DONTSPLASH;
	}
	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		A_SprayDecal("ImpactMark",20);
		int numpt = Random[Impact](20,40);
		Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (-x+(FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8),FRandom[Impact](-.8,.8))).unit()*FRandom[Impact](0.1,1.2);
			let s = Spawn("UTSmoke",pos);
			s.vel = pvel;
			s.SetShade(Color(1,1,1)*Random[Impact](128,192));
		}
		numpt = Random[Impact](4,12);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Impact](-1,1),FRandom[Impact](-1,1),FRandom[Impact](-1,1)).unit()*FRandom[Impact](2,8);
			let s = Spawn("UTSpark",pos);
			s.vel = pvel;
		}
		numpt = Random[Impact](4,16);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (FRandom[Impact](-1,1),FRandom[Impact](-1,1),FRandom[Impact](-1,1)).unit()*FRandom[Impact](2,8);
			let s = Spawn("UTChip",pos);
			s.vel = pvel;
		}
		Destroy();
	}
}

Class ImpactHammer : UTWeapon replaces Fist
{
	double chargesize, count;

	action void A_ResetCharge()
	{
		invoker.chargesize = 0;
		invoker.count = 0;
		invoker.FireEffect();	// intentional UT behavior
	}
	action void A_ChargeUp( int amt = 1 )
	{
		invoker.chargesize += (0.75*amt)/TICRATE;
		invoker.count += double(amt)/TICRATE;
		if ( invoker.count > 0.2 )
		{
			invoker.count = 0;
			A_AlertMonsters();
		}
		A_QuakeEx(clamp(invoker.chargesize*3,0,3),clamp(invoker.chargesize*3,0,3),clamp(invoker.chargesize*3,0,3),amt+1,0,96,"",QF_RELATIVE,rollIntensity:clamp(invoker.chargesize*0.3,0,0.3));
	}
	action void A_FireBlast()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		A_PlaySound("impact/release",CHAN_WEAPON);
		invoker.FireEffect();
		A_AlertMonsters();
		Vector3 x, y, z;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z;
		double realcharge = min(1.5,invoker.chargesize);
		FLineTraceData d;
		LineTrace(angle,80,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
		if ( d.HitType == TRACE_HitActor )
		{
			int dmg = Random[Impact](50,60)*realcharge;
			dmg = d.HitActor.DamageMobj(invoker,self,dmg,'impact');
			d.HitActor.vel = x*(1000/d.HitActor.mass)*realcharge;
			if ( d.HitActor.bNOBLOOD )
			{
				let p = Spawn("HammerImpact",d.HitLocation-d.HitDir*4);
				p.angle = atan2(d.HitDir.y,d.HitDir.x);
				p.pitch = asin(-d.HitDir.z);
			}
			else
			{
				d.HitActor.TraceBleed(dmg,invoker);
				d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
			}
		}
		else if ( d.HitType != TRACE_HitNone )
		{
			realcharge = max(1.0,realcharge);
			int dmg = Random[Impact](16,24)*realcharge;	// It's a flat damage of 36 on UT, but I think it's more fair for it to scale
			dmg = DamageMobj(invoker,self,dmg,'impact');
			TraceBleed(dmg,invoker);
			vel -= x*(1200/mass)*realcharge;
			let p = Spawn("HammerImpact",d.HitLocation-d.HitDir*4);
			p.angle = atan2(d.HitDir.y,d.HitDir.x);
			p.pitch = asin(-d.HitDir.z);
			if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
		}
		A_QuakeEx(realcharge*6,realcharge*6,realcharge*6,16,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:realcharge*0.2);
		realcharge = max(1.0,realcharge);
		int numpt = Random[Impact](5,10);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4))).unit()*FRandom[Impact](1,3)*realcharge;
			let s = Spawn("UTSmoke",origin);
			s.vel = pvel;
			s.SetShade(Color(1,1,1)*Random[Impact](128,192));
		}
		numpt = Random[Impact](4,8);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5))).unit()*FRandom[Impact](2,16)*realcharge;
			let s = Spawn("UTChip",origin);
			s.vel = pvel;
			s.scale *= 0.4;
		}
	}
	action void A_FireAltBlast()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		A_PlaySound("impact/fire",CHAN_WEAPON);
		invoker.FireEffect();
		A_AlertMonsters();
		A_QuakeEx(2,2,2,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.1);
		Vector3 x, y, z;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z;
		FLineTraceData d;
		LineTrace(angle,180,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
		double dscale = d.Distance/180.;
		if ( d.HitType == TRACE_HitActor )
		{
			int dmg = Random[Impact](16,20)*dscale;
			dmg = d.HitActor.DamageMobj(invoker,self,dmg,'impact');
			d.HitActor.vel = x*(500/d.HitActor.mass)*dscale;
		}
		else if ( d.HitType != TRACE_HitNone )
		{
			int dmg = Random[Impact](16,24)*dscale;
			dmg = DamageMobj(invoker,self,dmg,'impact');
			vel -= x*(600/mass)*dscale;
			if ( d.HitType == TRACE_HitWall ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation-d.HitDir*4);
		}
		// push aside projectiles
		let t = ThinkerIterator.Create("Actor");
		Actor m;
		while ( m = Actor(t.Next()) )
		{
			if ( !m.bMISSILE ) continue;
			double rdist = level.Vec3Diff(origin,m.pos).length();
			Vector3 rdir = level.Vec3Diff(origin,m.pos).unit();
			if ( LineTrace(atan2(rdir.y,rdir.x),rdist,asin(-rdir.z),TRF_THRUACTORS|TRF_ABSPOSITION,origin.z,origin.x,origin.y) || (rdist > 550) || (rdir dot x < 0.9) ) continue;
			m.speed = m.vel.length();
			if ( m.vel dot y > 0 ) m.vel = m.speed*(m.vel+(750-rdist)*y*0.01).unit();
			else m.vel = m.speed*(m.vel-(750-rdist)*y*0.01).unit();
			if ( m.target == self ) continue;
			if ( m.bSEEKERMISSILE ) m.tracer = m.target;
			m.target = self;
		}
		int numpt = Random[Impact](5,10);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4),FRandom[Impact](-.4,.4))).unit()*FRandom[Impact](1,3);
			let s = Spawn("UTSmoke",origin);
			s.vel = pvel;
			s.SetShade(Color(1,1,1)*Random[Impact](128,192));
		}
		numpt = Random[Impact](4,8);
		for ( int i=0; i<numpt; i++ )
		{
			Vector3 pvel = (x+(FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5),FRandom[Impact](-.5,.5))).unit()*FRandom[Impact](2,16);
			let s = Spawn("UTChip",origin);
			s.vel = pvel;
			s.scale *= 0.4;
		}
	}
	action void A_ImpactRefire( statelabel flash = null )
	{
		FLineTraceData d;
		Vector3 x, y, z;
		[x, y, z] = Matrix4.GetAxes(pitch,angle,roll);
		Vector3 origin = (pos.x,pos.y,player.viewz)+10.0*x+3.0*y-4.0*z;
		LineTrace(angle,40,BulletSlope(),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
		if ( (invoker.chargesize > 1) && (d.HitType == TRACE_HitActor) )
		{
			A_ClearRefire();
			return;
		}
		A_Refire(flash);
	}

	Default
	{
		Tag "Impact Hammer";
		Obituary "%o was smeared by %k's piston.";
		Inventory.PickupMessage "You got the Impact Hammer.";
		Weapon.UpSound "impact/select";
		Weapon.SlotNumber 1;
		Weapon.SelectionOrder 9;
		+WEAPON.MELEEWEAPON;
	}
	States
	{
	Spawn:
		IMPP A -1;
		Stop;
		IMPP B -1;
		Stop;
	Select:
		IMPS A 1 A_Raise(int.max);
		Wait;
	Ready:
		IMPS ABCDEFGHIJKLMNOPQ 1;
	Idle:
		IMPI A 1 A_WeaponReady();
		Wait;
	Fire:
		IMPL A 0
		{
			A_ResetCharge();
			A_PlaySound("impact/pull",CHAN_WEAPON);
		}
		IMPL A 5 A_ChargeUp(5);
		IMPL B 0 A_ImpactRefire(1);
		Goto Release;
		IMPL B 5 A_ChargeUp(5);
		IMPL C 0 A_ImpactRefire(1);
		Goto Release;
		IMPL C 5 A_ChargeUp(5);
		IMPL D 0 A_ImpactRefire(1);
		Goto Release;
		IMPL D 5 A_ChargeUp(5);
		IMPL E 0 A_ImpactRefire(1);
		Goto Release;
		IMPL E 5 A_ChargeUp(5);
		IMPL E 0 A_ImpactRefire(1);
		Goto Release;
		IMPL E 0 A_PlaySound("impact/loop",CHAN_WEAPON,looping:true);
	Hold:
		IMPR A 1 A_ChargeUp();
		IMPR B 0 A_ImpactRefire(1);
		Goto Release;
		IMPR B 1 A_ChargeUp();
		IMPR C 0 A_ImpactRefire(1);
		Goto Release;
		IMPR C 1 A_ChargeUp();
		IMPR D 0 A_ImpactRefire(1);
		Goto Release;
		IMPR D 1 A_ChargeUp();
		IMPR E 0 A_ImpactRefire(1);
		Goto Release;
		IMPR E 1 A_ChargeUp();
		IMPR F 0 A_ImpactRefire(1);
		Goto Release;
		IMPR F 1 A_ChargeUp();
		IMPR G 0 A_ImpactRefire(1);
		Goto Release;
		IMPR G 1 A_ChargeUp();
		IMPR H 0 A_ImpactRefire(1);
		Goto Release;
		IMPR H 1 A_ChargeUp();
		IMPR I 0 A_ImpactRefire(1);
		Goto Release;
		IMPR I 1 A_ChargeUp();
		IMPR J 0 A_ImpactRefire(1);
		Goto Release;
		IMPR J 1 A_ChargeUp();
		IMPR A 0 A_ImpactRefire("Hold");
		Goto Release;
	Release:
		IMPF A 0 A_FireBlast();
		IMPF AABCCDEEFGGHIIJKKLMMNOOP 1;
		Goto Idle;
	AltFire:
		IMPF A 0 A_FireAltBlast();
		IMPF ABCDEFGHIJKLMNOP 1;
		Goto Idle;
	Deselect:
		IMPD A 0 A_StopSound(CHAN_WEAPON);
		IMPD ABCDE 3;
		IMPD E 1 A_Lower(int.max);
		Wait;
	}
}
