Class URifleAmmo : Ammo
{
	Default
	{
		Tag "$T_RIFLEAMMO";
		Inventory.Icon "I_RifleA";
		Inventory.PickupMessage "";
		Inventory.Amount 8;
		Inventory.MaxAmount 50;
		Ammo.BackpackAmount 4;
		Ammo.BackpackMaxAmount 100;
		Ammo.DropAmount 8;
		+INVENTORY.IGNORESKILL;
	}
	override String PickupMessage()
	{
		if ( PickupMsg.Length() > 0 ) return Super.PickupMessage();
		return String.Format("%s%d%s",StringTable.Localize("$I_RIFLEAMMOL"),Amount,StringTable.Localize("$I_RIFLEAMMOR"));
	}
	States
	{
	Spawn:
		SBOX A -1;
		Stop;
	}
}

Class URifleAmmo2 : URifleAmmo
{
	Default
	{
		Tag "$T_RIFLEAMMO2";
		Inventory.PickupMessage "$I_RIFLEAMMO2";
		Inventory.Amount 1;
		Ammo.DropAmount 1;
		+INVENTORY.IGNORESKILL;
	}
	States
	{
	Spawn:
		SRND A -1;
		Stop;
	}
}

Class URifle : UnrealWeapon
{
	double sniperzoom;
	ui TextureID reticle;
	transient ui Font zfont;
	bool bLightOn;
	UFlashLight1 lt[2];

	override void PreRender( double lbottom )
	{
		if ( sniperzoom <= 1. ) return;
		if ( reticle.IsNull() ) reticle = TexMan.CheckForTexture("UReticle",Texman.Type_Any);
		Screen.DrawTexture(reticle,false,320,320,DTA_VirtualWidth,640,DTA_VirtualHeight,640,DTA_LegacyRenderStyle,STYLE_Stencil,DTA_FillColor,Color(255,0,0));
		if ( !zfont ) zfont = Font.GetFont('UTFont40');
		Screen.DrawText(zfont,Font.CR_UNTRANSLATED,700,700,String.Format("X%.1f",sniperzoom),DTA_VirtualWidth,1280,DTA_VirtualHeight,960,DTA_LegacyRenderStyle,STYLE_Stencil,DTA_FillColor,Color(255,0,0));
	}
	override void DetachFromOwner()
	{
		Super.DetachFromOwner();
		PlayerInfo p = players[consoleplayer];
		if ( p.Camera == Owner ) PPShader.SetEnabled("URifleScope",false);
		bLightOn = false;
		if ( lt[0] ) lt[0].Destroy();
		if ( lt[1] ) lt[1].Destroy();
	}
	override void RenderOverlay( RenderEvent e )
	{
		PlayerInfo p = players[consoleplayer];
		if ( (p.Camera != Owner) || (sniperzoom <= 1.) ) PPShader.SetEnabled("URifleScope",false);
		else PPShader.SetEnabled("URifleScope",sting_zoomshader);
	}
	override void DoEffect()
	{
		Super.DoEffect();
		bALT_AMMO_OPTIONAL = !sting_rifle;
		if ( sniperzoom > 1.0 ) crosshair = 99;
		else crosshair = 0;
	}
	action void A_ToggleLight()
	{
		invoker.bLightOn = !invoker.bLightOn;
		A_StartSound(invoker.bLightOn?"lite/pickup":"lite/off",CHAN_ITEM);
		if ( invoker.bLightOn )
		{
			if ( !invoker.lt[0] ) invoker.lt[0] = UFlashLight1(Spawn("UFlashLight1",pos));
			invoker.lt[0].target = self;
			invoker.lt[0].master = invoker;
			invoker.lt[0].basecolor[0] = 255;
			invoker.lt[0].basecolor[1] = 224;
			invoker.lt[0].basecolor[2] = 192;
			invoker.lt[0].args[3] = 480;
			invoker.lt[0].SpotInnerAngle = 2;
			invoker.lt[0].SpotOuterAngle = 9;
			if ( !invoker.lt[1] ) invoker.lt[1] = UFlashLight1(Spawn("UFlashLight2",pos));
			invoker.lt[1].target = self;
			invoker.lt[1].master = invoker;
			invoker.lt[1].basecolor[0] = 128;
			invoker.lt[1].basecolor[1] = 112;
			invoker.lt[1].basecolor[2] = 96;
			invoker.lt[1].args[3] = 500;
			invoker.lt[1].SpotOuterAngle = 15;
		}
		else
		{
			if ( invoker.lt[0] ) invoker.lt[0].Destroy();
			if ( invoker.lt[1] ) invoker.lt[1].Destroy();
		}
	}
	action void A_RifleFire( bool zoomed = false, bool alt = false )
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( weap.Ammo1.Amount <= 0 ) return;
		if ( !weap.DepleteAmmo(weap.bAltFire,true,1) ) return;
		if ( self is 'UTPlayer' )
			UTPlayer(self).PlayAttacking3();
		invoker.FireEffect();
		UTMainHandler.DoFlash(self,Color(32,0,0,255),1);
		if ( alt ) A_StartSound("rifle/fire",CHAN_WEAPON,CHANF_OVERLAP,Dampener.Active(self)?.3:1.,pitch:FRandom[Sniper](0.9,1.1));
		else A_StartSound("rifle/fire",CHAN_WEAPON,CHANF_OVERLAP,Dampener.Active(self)?.3:1.);
		if ( !Dampener.Active(self) ) A_AlertMonsters();
		if ( zoomed ) A_QuakeEx(1,1,1,2,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.09);
		else
		{
			A_QuakeEx(2,2,2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12);
			A_Overlay(-2,"MuzzleFlash");
			A_OverlayFlags(-2,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
			A_OverlayRenderstyle(-2,STYLE_Add);
		}
		let l = Spawn("SniperLight",pos);
		l.target = self;
		Vector3 x, y, z, x2, y2, z2, dir;
		[x, y, z] = dt_Utility.GetAxes(angle,pitch,roll);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x);
		if ( !zoomed ) origin = level.Vec3Offset(origin,-y*3-z*2);
		FLineTraceData d;
		[x2, y2, z2] = dt_Utility.GetAxes(angle,BulletSlope(),roll);
		if ( alt ) dir = dt_Utility.ConeSpread(x2,y2,z2,FRandom[Rifle](0,360),FRandom[Rifle](0.,.25));
		else dir = x2;
		LineTrace(atan2(dir.y,dir.x),10000,asin(-dir.z),TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
		UTBulletTrail.DoTrail(self,origin,dir,10000,alt?3:1);
		if ( d.HitType == TRACE_HitActor )
		{
			int dmg = 45;
			if ( d.HitLocation.z >= (d.HitActor.pos.z+d.HitActor.height*0.81) )
			{
				dmg = d.HitActor.DamageMobj(invoker,self,100,'Decapitated',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
				UTMainHandler.DoKnockback(d.HitActor,d.HitDir,35000);
			}
			else
			{
				dmg = d.HitActor.DamageMobj(invoker,self,dmg,'shot',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
				UTMainHandler.DoKnockback(d.HitActor,d.HitDir,30000);
			}
			if ( d.HitActor.bNOBLOOD )
			{
				let p = Spawn("BulletImpact",d.HitLocation);
				p.scale *= 1.5;
				p.angle = atan2(d.HitDir.y,d.HitDir.x)+180;
				p.pitch = asin(d.HitDir.z);
			}
			else
			{
				d.HitActor.TraceBleed(dmg,self);
				d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
			}
		}
		else if ( d.HitType != TRACE_HitNone )
		{
			Vector3 hitnormal = -d.HitDir;
			if ( d.HitType == TRACE_HitFloor )
			{
				if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
				else hitnormal = d.HitSector.floorplane.Normal;
			}
			else if ( d.HitType == TRACE_HitCeiling )
			{
				if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
				else hitnormal = d.HitSector.ceilingplane.Normal;
			}
			else if ( d.HitType == TRACE_HitWall )
			{
				hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
				if ( !d.LineSide ) hitnormal *= -1;
			}
			let p = Spawn("BulletImpact",d.HitLocation+hitnormal*0.01);
			p.scale *= 1.5;
			p.angle = atan2(hitnormal.y,hitnormal.x);
			p.pitch = asin(-hitnormal.z);
			if ( d.HitLine ) d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation);
		}
		for ( int i=0; i<24; i++ )
		{
			let s = Spawn("UTStaticViewSmoke",origin);
			UTViewSmoke(s).ofs = (10,-3,-2);
			UTViewSmoke(s).vvel += (FRandom[Sniper](-0.05,0.15),FRandom[Sniper](-1.2,1.2),FRandom[Sniper](-0.1,0.1));
			s.target = self;
			s.scale *= 1.8;
			s.alpha *= 0.3;
		}
		origin = level.Vec3Offset(origin,x*4-y*6-z*10);
		let c = Spawn("UCasing",origin);
		c.scale *= 1.25;
		c.angle = angle;
		c.pitch = pitch;
		c.vel = x*FRandom[Junk](-1.5,1.5)+y*FRandom[Junk](2,4)+z*FRandom[Junk](2,3);
	}
	override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
	{
		if ( mod == 'Decapitated' ) return StringTable.Localize("$O_SNIPERDECAP");
		return Obituary;
	}
	Default
	{
		Tag "$T_RIFLE";
		Obituary "$O_SNIPER";
		Inventory.PickupMessage "$I_RIFLE";
		Weapon.UpSound "rifle/select";
		Weapon.SlotNumber 9;
		Weapon.SelectionOrder 900;
		Weapon.SlotPriority 1;
		Weapon.AmmoType "URifleAmmo";
		Weapon.AmmoUse 1;
		Weapon.AmmoType2 "URifleAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 8;
		Weapon.Kickback 250;
		UTWeapon.DropAmmo 4;
		+NOEXTREMEDEATH;
	}
	States
	{
	Spawn:
		SRFP A -1;
		Stop;
		SRFP B -1;
		Stop;
	Select:
		SRFS A 1 A_Raise(int.max);
		Wait;
	Ready:
		SRFS A 0 A_ZoomFactor(invoker.sniperzoom=1.0,ZOOM_INSTANT);
		SRFS ABCDEFGHIJKLMNOPQRST 1 A_WeaponReady(WRF_NOFIRE);
	Idle:
		SRFI A 1
		{
			A_CheckReload();
			A_WeaponReady(sting_riflel?(WRF_ALLOWZOOM|WRF_ALLOWRELOAD):WRF_ALLOWZOOM);
		}
		Wait;
	ZoomedIdle:
		TNT1 A 1
		{
			A_CheckReload();
			A_WeaponReady(sting_riflel?(WRF_ALLOWZOOM|WRF_ALLOWRELOAD):WRF_ALLOWZOOM);
		}
		Wait;
	Fire:
		SRFF A 0 A_JumpIf(invoker.sniperzoom>1.0,"ZoomedFire");
		SRFF A 0 A_RifleFire();
		SRFF ABCDEFGHIJ 2;
		Goto Idle;
	ZoomedFire:
		TNT1 A 20 A_RifleFire(true);
		Goto ZoomedIdle;
	AltFire:
		SRFF A 0 A_JumpIf(!sting_rifle,"Zoom");
		SRFF A 0 A_JumpIf(invoker.sniperzoom>1.0,"ZoomedAltFire");
		SRFF A 0 A_RifleFire(false,true);
		SRFF ABCDEFG 1;
		SRFF A 0 A_JumpIfNoAmmo("AltFireEnd");
		SRFF A 0 A_RifleFire(false,true);
		SRFF ABCDEFG 1;
		SRFF A 0 A_JumpIfNoAmmo("AltFireEnd");
		SRFF A 0 A_RifleFire(false,true);
		SRFF ABCDEFG 1;
	AltFireEnd:
		SRFF HIJ 1;
		SRFI A 3;
		Goto Idle;
	ZoomedAltFire:
		TNT1 A 7 A_RifleFire(true,true);
		TNT1 A 0 A_JumpIfNoAmmo("ZoomedAltFireEnd");
		TNT1 A 7 A_RifleFire(true,true);
		TNT1 A 0 A_JumpIfNoAmmo("ZoomedAltFireEnd");
		TNT1 A 7 A_RifleFire(true,true);
	ZoomedAltFireEnd:
		TNT1 A 6;
		Goto ZoomedIdle;
	Reload:
		SRFI A 0 A_JumpIf(invoker.sniperzoom>1.0,"ZoomedReload");
		SRFI A 8 A_ToggleLight();
		Goto Idle;
	ZoomedReload:
		TNT1 A 8 A_ToggleLight();
		Goto Idle;
	Zoom:
		SRSU A 0 A_JumpIf(invoker.sniperzoom>1.0,"ZoomOut");
		SRSU A 0 A_StartSound("rifle/scopeon",CHAN_WEAPONMISC,volume:.5);
		SRSU ABCDEFGHIJKLMN 1;
		SRSI A 0;
		Goto ZoomHold;
	ZoomHold:
		TNT1 A 1
		{
			if ( invoker.sniperzoom <= 8.0 )
				A_ZoomFactor(invoker.sniperzoom*=1.1);
			invoker.sniperzoom = min(invoker.sniperzoom,8.1);
		}
		TNT1 A 0 A_JumpIf(player.cmd.buttons&(BT_ALTATTACK|BT_ZOOM),"ZoomHold");
		Goto ZoomedIdle;
	ZoomOut:
		SRSD A 0 A_ZoomFactor(invoker.sniperzoom=1.0,ZOOM_INSTANT);
		SRSD A 0 A_StartSound("rifle/scopeoff",CHAN_WEAPONMISC,volume:.5);
		SRSI A 1;
		SRSD ABCDEFGHIJKLMNO 1;
		Goto Idle;
	Deselect:
		SRFD A 0 A_JumpIf(invoker.sniperzoom<=1.0,"Deselect2");
		SRSD A 0 A_ZoomFactor(invoker.sniperzoom=1.0,ZOOM_INSTANT);
		SRSD A 0 A_StartSound("rifle/scopeoff",CHAN_WEAPONMISC,volume:.5);
		SRSI A 1;
		SRSD ABCDEFGHIJKLMNO 1;
	Deselect2:
		SRFD A 0
		{
			A_ZoomFactor(invoker.sniperzoom=1.0,ZOOM_INSTANT);
			if ( invoker.bLightOn ) A_ToggleLight();
		}
		SRFD ABCDEFG 1;
		SRFD H 1 A_Lower(int.max);
		Wait;
	MuzzleFlash:
		SMUZ A 3 Bright;
		Stop;
	}
}
