Class UTranslocatorAmmo : Ammo
{
	Default
	{
		Inventory.Icon "I_Tele";
		Inventory.Amount 1;
		Inventory.MaxAmount 2;
		Ammo.BackpackAmount 0;
		Ammo.BackpackMaxAmount 2;
		+INVENTORY.IGNORESKILL;
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto && sting_telegun ) return;
		if ( Owner ) Owner.RemoveInventory(self);
		Destroy();
	}
}

Class UTranslocatorModule : Actor
{
	Actor b;
	bool alreadyhit;
	double anglevel;

	Default
	{
		Radius 2;
		Height 2;
		Speed 16;
		PROJECTILE;
		-NOGRAVITY;
		+USEBOUNCESTATE;
		+SKYEXPLODE;
		+HITTRACER;
		+MOVEWITHSECTOR;
		+CANBOUNCEWATER;
		+BLOCKASPLAYER;
		-ALLOWBOUNCEONACTORS;
		+DONTBOUNCEONSHOOTABLES;
		+INTERPOLATEANGLES;
		BounceType "Hexen";
		BounceFactor 0.3;
		WallBounceFactor 0.3;
		Gravity 0.35;
	}

	override void PostBeginPlay()
	{
		Super.PostBeginPlay();
		if ( !target )
		{
			Destroy();
			return;
		}
		b = Spawn("ModuleHitbox",pos);
		b.A_SetSize(6,6);
		b.master = self;
		anglevel = FRandom[TeleHand](3,5)*RandomPick[TeleHand](-1,1);
	}

	override bool CanCollideWith( Actor other, bool passive )
	{
		return (other != tracer);
	}

	override void Tick()
	{
		Super.Tick();
		if ( bAMBUSH && !Random[Transloc](0,40) )
		{
			A_PlaySound("transloc/spark");
			int numpt = Random[Transloc](20,40);
			for ( int i=0; i<numpt; i++ )
			{
				Vector3 pvel = (FRandom[Transloc](-1,1),FRandom[Transloc](-1,1),FRandom[Transloc](-1,1)).unit()*FRandom[Transloc](0.1,1.2);
				let s = Spawn("UTSmoke",pos);
				s.vel = pvel;
				s.SetShade(Color(1,1,1)*Random[Transloc](128,192));
			}
			numpt = Random[Transloc](4,12);
			for ( int i=0; i<numpt; i++ )
			{
				Vector3 pvel = (FRandom[Transloc](-1,1),FRandom[Transloc](-1,1),FRandom[Transloc](-1,1)).unit()*FRandom[Transloc](2,8);
				let s = Spawn("UTSpark",pos);
				s.vel = pvel;
			}
		}
	}

	States
	{
	Spawn:
		TMOD A 1
		{
			angle += anglevel;
		}
		Wait;
	Bounce:
		TMOD A 0
		{
			if ( alreadyhit )
			{
				ClearBounce();
				ExplodeMissile();
			}
			else
			{
				anglevel = FRandom[TeleHand](5,30)*RandomPick[TeleHand](-1,1);
				A_PlaySound("transloc/bounce");
				if ( BlockingFloor || (tracer && (pos.z >= tracer.pos.z+tracer.height) && tracer.bACTLIKEBRIDGE) ) alreadyhit = true;
			}
		}
		Goto Spawn;
	Death:
		TMOD A -1
		{
			if ( tracer && !tracer.bACTLIKEBRIDGE )
			{
				SetOrigin(tracer.Vec2OffsetZ(0,0,pos.z),false);
				vel.xy *= 0;
				tracer = null;
				bHITTRACER = false;
			}
		}
		Stop;
	}
}

Class UTranslocator : UnrealWeapon
{
	Actor module;

	override bool TryPickup( in out Actor toucher )
	{
		if ( !sting_proto || !sting_telegun ) return false;	// not allowed
		return Super.TryPickup(toucher);
	}
	override void Tick()
	{
		Super.Tick();
		if ( sting_proto && sting_telegun ) return;
		if ( Owner ) Owner.RemoveInventory(self);
		Destroy();
	}
	override void DoEffect()
	{
		Super.DoEffect();
		if ( Ammo1 ) Ammo1.Amount = module?1:2;
	}
	action void A_ThrowModule()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		A_PlaySound("telehand/throw",CHAN_WEAPON);
		invoker.FireEffect();
		UTMainHandler.DoSwing(self,(FRandom[Translocator](0.2,-0.4),FRandom[Translocator](0.2,-0.7)),2,-0.3,3,SWING_Spring,2,3);
		Vector3 x, y, z;
		[x, y, z] = dt_CoordUtil.GetAxes(pitch,angle,roll);
		Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x-8*y-12*z);
		let p = Spawn("UTranslocatorModule",origin);
		p.target = self;
		p.angle = angle;
		p.pitch = BulletSlope();
		p.A_SetSize(radius);
		Vector3 dir = (cos(p.angle)*cos(p.pitch),sin(p.angle)*cos(p.pitch),-sin(p.pitch));
		dir.z += 0.35*(1-abs(dir.z));
		p.vel = dir*p.speed;
		invoker.module = p;
	}
	action void A_ReturnModule()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		A_PlaySound("transloc/return",CHAN_WEAPON);
		invoker.FireEffect();
		UTMainHandler.DoSwing(self,(FRandom[Translocator](-0.2,-0.4),FRandom[Translocator](-0.2,0.7)),3,-1,2,SWING_Spring,2,2);
		if ( invoker.module && invoker.module.bAMBUSH )
		{
			UTMainHandler.DoFlash(self,Color(255,255,255,255),50);
			A_PlaySound("transloc/spark",CHAN_WEAPON);
			DamageMobj(invoker,self,int.max,'Telefrag',DMG_THRUSTLESS);
		}
		if ( invoker.module ) invoker.module.Destroy();
	}
	action void A_Translocate()
	{
		Weapon weap = Weapon(invoker);
		if ( !weap ) return;
		if ( !invoker.module )
		{
			invoker.FireEffect();
			A_PlaySound("transloc/return",CHAN_WEAPON);
			return;
		}
		// check if there's enough space
		Vector3 oldpos = pos, newpos = invoker.module.pos;
		double modulefloorz = invoker.module.floorz, moduleceilingz = invoker.module.ceilingz;
		bool bBroken = invoker.module.bAMBUSH;
		invoker.module.Destroy();
		invoker.FireEffect();
		// squeeze down new z if ceiling is in the way
		if ( (newpos.z+height > moduleceilingz) ) newpos.z = max(modulefloorz,moduleceilingz-height);
		// temporarily disable telefragging for all allies
		bool oldnotele[MAXPLAYERS];
		for ( int i=0; i<MAXPLAYERS; i++ )
		{
			if ( !playeringame[i] || !players[i].mo ) continue;
			oldnotele[i] = players[i].mo.bNOTELEFRAG;
			if ( !IsFriend(players[i].mo) ) continue;
			players[i].mo.bNOTELEFRAG = true;
		}
		if ( Warp(self,newpos.x,newpos.y,newpos.z,flags:WARPF_ABSOLUTEPOSITION|WARPF_TESTONLY) && TeleportMove(newpos,true) )
		{
			A_PlaySound("flak/click",CHAN_WEAPON);
			SpawnTeleportFog(oldpos,true,false);
			SpawnTeleportFog(newpos,false,false);
			player.fov = min(175,player.desiredfov+60);
		}
		else A_PlaySound("transloc/return",CHAN_WEAPON);
		for ( int i=0; i<MAXPLAYERS; i++ )
		{
			if ( !playeringame[i] || !players[i].mo ) continue;
			players[i].mo.bNOTELEFRAG = oldnotele[i];
		}
		if ( bBroken )
		{
			UTMainHandler.DoFlash(self,Color(255,255,255,255),50);
			A_PlaySound("transloc/spark",CHAN_WEAPON);
			DamageMobj(invoker,self,int.max,'Telefrag',DMG_THRUSTLESS);
		}
	}
	override void OnDestroy()
	{
		Super.OnDestroy();
		if ( module ) module.Destroy();
	}

	Default
	{
		Tag "$T_TELEGUN";
		Inventory.PickupMessage "$I_TELEGUN";
		Weapon.SlotNumber 1;
		Weapon.SelectionOrder 9000;
		Weapon.SlotPriority 0.1;
		Weapon.AmmoType1 "UTranslocatorAmmo";
		Weapon.AmmoUse1 1;
		Weapon.AmmoType2 "UTranslocatorAmmo";
		Weapon.AmmoUse2 1;
		Weapon.AmmoGive 2;
		UTWeapon.DropAmmo 2;
		+WEAPON.NO_AUTO_SWITCH;
		+WEAPON.CHEATNOTWEAPON;
		+INVENTORY.UNDROPPABLE;
		+INVENTORY.UNTOSSABLE;
	}
	States
	{
	Spawn:
		TLCP A -1;
		Stop;
		TLCP B -1;
		Stop;
	Select:
		TLCS A 1 A_Raise(int.max);
		Wait;
	Ready:
		TLCS ABCDEFGH 2 A_WeaponReady(WRF_NOFIRE);
		Goto Idle;
	Dummy:
		TNT1 A 1 A_WeaponReady(WRF_ALLOWRELOAD);
		Wait;
	Idle:
		TLCI A 0 A_Overlay(-9999,"Dummy");
		TLCI ABCDEFG 20 A_Jump(16,"Twiddle");
		Goto Idle+1;
	Twiddle:
		#### # 6 { invoker.special1 = Random[TeleHand](2,5); }
		TLCT ABCDEFGHI 3 A_SetTics(invoker.special1);
		Goto Idle+1;
	Fire:
		TLCF A 0 A_Overlay(-9999,"Null");
		TLCF A 0 A_JumpIf(invoker.module,"Recall");
		TLCF ABCDEF 2;
		TLCF G 0 A_ThrowModule();
		TLCF GHIJKLMNOPQRSTUVWXY 2;
		Goto Idle;
	Recall:
		TLCR ABCDEF 2;
		TLCR G 0 A_ReturnModule();
		TLCR GHIJKLM 2;
		Goto Idle;
	AltFire:
		TLCA A 0 A_Overlay(-9999,"Null");
		TLCA A 0 A_JumpIf(!invoker.module,"Reload");
		TLCA ABCDE 2;
		TLCA A 0 A_Translocate();
		TLCA FGHIJKL 2;
		Goto Idle;
	Reload:
		TLCB A 0 A_Overlay(-9999,"Null");
		TLCB ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 2;
		TLB2 AB 2;
		Goto Idle;
	Deselect:
		TLCD A 0 A_Overlay(-9999,"Null");
		TLCD ABCDEF 1;
		TLCD F 1 A_Lower(int.max);
	}
}
